Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flow port #13

Open
KiaraGrouwstra opened this issue Feb 15, 2018 · 21 comments
Open

Flow port #13

KiaraGrouwstra opened this issue Feb 15, 2018 · 21 comments

Comments

@KiaraGrouwstra
Copy link
Owner

KiaraGrouwstra commented Feb 15, 2018

Flow offers things like $Call which have been a bottleneck for me so far. I should finish up my flow branch to get things up to parity. I'm not actually good at Flow so help welcome!

edit: I should check type-at-pos to find the inferred types.

@goodmind
Copy link

goodmind commented Feb 1, 2019

@tycho01 I have found a way to iterate for unions

type A = 'a' | 'b' | 'c'
type D = {[a: A]: any}

declare var d: $ObjMap<D, () => string>

d.a
d.b
d.c

const s = d.unknown 
// this is still string but access errors
/*
10: const s = d.unknown
              ^ string `unknown` [1] is incompatible with enum [2].
References:
10: const s = d.unknown
              ^ [1]
2: type D = {[a: A]: any}
                 ^ [2]
*/

@KiaraGrouwstra
Copy link
Owner Author

yeah $ObjMap seems great 😅, definitely seems one of the main features of Flow over TS (next to $Call, $TupleMap)

@goodmind
Copy link

goodmind commented Feb 7, 2019

@tycho01 I thought conditional types is basically $Call?

@KiaraGrouwstra
Copy link
Owner Author

@goodmind: you're not far off! it has two use-cases:

  • pattern matching, within TS now covered by conditional types;
  • calculating types based on an input function, needed for e.g. map on tuples / heterogeneous objects (both already covered in Flow utilities), as well as for e.g. function composition and currying.

This is why I was kinda sad they gave us conditional types instead of $Call, as it means the second use-case is likely still gonna remain pretty far off. And since those are pretty vital for FP libs like Ramda, that's why I'd feel more optimistic about Flow at this point.

@goodmind
Copy link

goodmind commented Mar 20, 2019

@tycho01 hey! I implemented $Required in Flow and it allows to implement Pick in userland ($Rest doesn't remove optional properties because it is unsound in runtime, but we are at type-level :P)
facebook/flow#7571

type RestFixed<T1, T2> = $Rest<T1, $Required<T2>>
type Pick<T1, T2> = RestFixed<
  T1,
  $ObjMap<RestFixed<T1, T2>, <V>(V) => any>
>;

declare var picked: Pick<{| a?: number, b?: string, c: string |}, {| a: any |}>;

picked.a; // void | number
picked.b; // error
picked.c; // error

@goodmind
Copy link

@tycho01 with latest generic function inference in TypeScript it seems like Flow now far off from typing Ramda :(

@KiaraGrouwstra
Copy link
Owner Author

KiaraGrouwstra commented Mar 22, 2019

that looks pretty cool! :D

with latest generic function inference in TypeScript it seems like Flow now far off from typing Ramda :(

Do you mean stuff like R.pipe(R.identity) that got better in recent TS isn't as good yet in Flow?

Like, how do you see the major differences in expressiveness in TS and Flow now? Is it mostly about some of the TS additions like infer?

edit: P.S. if you have a few types in a repo I can link it from the readme here, or alternatively I can give you push access over here if you want.

@goodmind
Copy link

goodmind commented Mar 22, 2019

@tycho01 infer can still be done with $Call but new generic inference microsoft/TypeScript#30193, microsoft/TypeScript#30215 can't, but Flow now is more active in community than before

Also, I don't have typelevel stuff repository, because I'm not a fun of too much complex typelevel stuff after I've seen what styled-components does in TS
image
💯

#27 also this lol

I often write simple flow code, and if something can't be typed I fork library

@KiaraGrouwstra
Copy link
Owner Author

Yeah, type-level coding sucks, admittedly.
In an ideal world, I hope we could just get down the hard stuff down once, after which end-users can just automagically enjoy the benefits without having to understand freaky type magic.

I'm shocked about the styled-components example as well! Not because I haven't seen complex types before, obviously, but I find it surprising that a library at their level would need to make types this convoluted. I'd wonder if I'm missing something or if they really need it.
My idea had been perhaps most libraries could just use well-typed variables/functions instead of having to write their own custom type magic. Definitely curious as to what limitations forced them into this!

@goodmind
Copy link

goodmind commented Mar 23, 2019

@tycho01 I was playing around OCaml and wrote this 🔥

image

Any examples I can implement with this?

@KiaraGrouwstra
Copy link
Owner Author

KiaraGrouwstra commented Mar 23, 2019

hahaha, I'm quite confused about what happened in your example, but a type-level reduce is pretty cool! that's definitely among the remaining challenges in TS. I listed some potential use-cases for this based on Ramda here, though part may be solved already.

@goodmind
Copy link

goodmind commented Mar 23, 2019

@tycho01 I just hardcoded reducing into object

| DefT (_, trust, ArrT arrtype), MapTypeT (reason_op, Reduce funt, tout) ->
      let props = match arrtype with
      | ArrayAT (_, opt) -> 
        (match opt with
          | Some ts -> 
            Core_list.fold_left ts ~f:(fun acc value ->
              match value with
                | DefT (_, _, SingletonStrT str) -> 
                  let t = EvalT (funt, TypeDestructorT (unknown_use, reason_op, CallType [value]), mk_id ()) in
                  SMap.add str (Field (None, t, Positive)) acc
                | _ -> acc
            ) ~init:SMap.empty
          | None -> SMap.empty)
      | TupleAT (_, ts) -> 
        Core_list.fold_left ts ~f:(fun acc value -> 
          match value with
            | DefT (_, _, SingletonStrT str) -> 
              let t = EvalT (funt, TypeDestructorT (unknown_use, reason_op, CallType [value]), mk_id ()) in
              SMap.add str (Field (None, t, Positive)) acc
            | _ -> acc
        ) ~init:SMap.empty
      | ROArrayAT (_) -> SMap.empty in
      let props_tmap = Context.make_property_map cx props in
      let t =
        let reason = replace_reason_const RObjectType reason_op in
        let proto_t = ObjProtoT reason in
        let call_t = None in
        let dict_t = None in
        let flags = {
          exact = true;
          sealed = Sealed;
          frozen = false;
        } in
        DefT (reason, trust, ObjT {flags; dict_t; proto_t; props_tmap; call_t;})
        (* Obj_type.mk_with_proto cx reason ~frozen:true ~sealed:true ~exact:true ~props proto *)
      in
      rec_flow_t cx trace (t, tout)

, perhaps this is more limiting to implement something like path.

Not sure how would initial value behave if we can't subtract and add types?

// build object
var s: $Reduce<["a", "b"], <Acc, K>(Acc, K) => {
   ...Acc,
   [K]: K // sorry no computed props in Flow 
}, {||}> // failed example, should be special cased then

// build union without initial value
var s: $Reduce<["a", "b"], <Acc, K>(Acc, K) => Acc | K> // "a" | "b"

And this quickly becomes more complicated to implement

TypeScript in general allows more userland type magic, because all type utilities work well together but this isn't like this in Flow, $Values breaks this $Reduce example if you use switch on resulting union

@goodmind
Copy link

goodmind commented Mar 23, 2019

Working example for second reduce attempt, without initial value, because no idea how to do optional parameters

image

@KiaraGrouwstra
Copy link
Owner Author

hm. I thought you showed me a path Flow implementation before, using compose stuff to achieve the reduce-like functionality...

@goodmind
Copy link

@tycho01, but Compose implementation doesn't work if you export function

@KiaraGrouwstra
Copy link
Owner Author

really? I'm probably missing some context but that sounds kinda lame. :/

@goodmind
Copy link

goodmind commented Mar 24, 2019

Yeah, all exports should be annotated properly for performance reasons, and you can't do this with compose(...spread). It would've worked if there was explicit $ComposeTuple, but $Compose is variadic

@goodmind
Copy link

goodmind commented Mar 24, 2019

I managed to do initial value, but didn't found how to resolve generics for third argument :/
So it is pretty useless now
IMG_20190324_221919_895

Aka flow would crash if you pass generic to third argument

@KiaraGrouwstra
Copy link
Owner Author

ah, I see, thanks!
if Flow has issues as well, then I guess we're probably boned for the time being. 😂

@goodmind
Copy link

There is also discord server with Flow maintainers, usually they help with contributing. I managed to add BigInt parsing and $Required type only with their help
https://discordapp.com/invite/8ezwRUK

@KiaraGrouwstra
Copy link
Owner Author

whoa, that's pretty nice!
It's felt like the TS team has been fairly swamped with stuff, though at least recently Ramda got some attention there as it was the only typing repo convoluted enough to break on their recent changes. 😂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants