emmanueltouzery / prelude-ts

Functional programming, immutable collections and FP constructs for typescript and javascript
ISC License
377 stars 21 forks source link

Help with preserving order in HashSet #51

Open ghost opened 3 years ago

ghost commented 3 years ago

The HashSet is nice to get equality checks. But when you are using it to display UI elements, vector/array is preferable since order of elements seems to change on each re-render of the application. But vector doesn't have the nice properties of HashSet.

Is there some way to get a LinkedHashSet like functionality (Something like: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-linked-hash-set/)?

emmanueltouzery commented 3 years ago

yes for that we'd need a new data structure like LinkedHashSet or TreeSet. It is missing in prelude currently, that's true, and it would be a nice feature to have. Actually TreeSet enforces the same sorting order always, while LinkedHashSet keeps the one you give, so presumably LinkedHashSet would be the most interesting here. I'm not sure how much work would that be, presumably not that little :(

ghost commented 3 years ago

No worries. Would use some sorting after using toArray() on a HashSet for now. You can focus on easy to implement features first.

emmanueltouzery commented 3 years ago

ah, I mean the library is actually relatively in maintenance mode at this point: it works great and the API works for me. but maybe such features can be added. i need to find some time for it. need to find out how linked hashmaps work, decide whether we'd roll our own, or reuse some library...

ghost commented 3 years ago

okay. Initially when I was starting with prelude-ts, ran into some problems with folding over Vector/HashSet, remember it complaining about something where I believe it should not have. Javascript's reduce was a little more relaxed but still had some limitations. I will try to remember and report what it was here. One pattern that I use to write programs is to have an acumulator that is mutating as structure is being folded, the issue was something realted to that.

ghost commented 3 years ago

Not sure if there are any Typescript libraries out there that provide 'if' and 'case/guard' expressions, since they are clearly preferable over 'if' and 'switch' statements provided by the javascript. I tried to come up with something and using below. My Typescript knowledge is limited and below functions when used as expressions suffers from types not being narrowed, but it would have been nice if these basic blocks could be provided out of the box.

type X<T> = T | (() => X<T>)

function R<V>(expr: X<V>): V {
    if (typeof expr === 'function') {
        return R((expr as () => X<V>)())
    } else {
        return (expr as V)
    }
}

export function when<T extends string, V>(expression: X<T>, guards: { [key in T]: X<V> }): V {
    const expr = typeof expression === 'function' ? R(expression as () => X<T>) : (expression as T)
    return typeof guards[expr] === 'function' ? R(guards[expr] as () => X<V>) : (guards[expr] as V)
}

export function iff<V>(predicate: X<boolean>, expr1: X<V>, expr2: X<V>): V {
    if (typeof predicate === 'function' ? R(predicate) : predicate) {
        return typeof expr1 === 'function' ? R(expr1 as () => X<V>) : (expr1 as V)
    } else {
        return typeof expr2 === 'function' ? R(expr2 as () => X<V>) : (expr2 as V)
    }
}

X is recursive but did not find its recursive nature useful until now.

emmanueltouzery commented 3 years ago

no, I feel such basic control flow statements don't belong in prelude. We have combinators (like combining two options, things like that, a). but basic general-purpose control flow, i don't see it belonging in prelude.