You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
It's been 2 years that I'm playing with higher order functions and kinds. This is part of my effort on creating a course of "Functional Programming with TypeScript". You can find the content here.
I'm working on a next video to teach "Functor composition", but I'm getting to a strange behavior on argument inference that I cannot reason about. You have an example of what I'm working on (compose function) in the docs here.
What baffles me is why swapping place of arguments in the compose function, makes my examples for higher kinded types fail. I've explained this completely in a question with a sample snippet of code and playground in StackOverFlow here. I'm trying this with the latest version of TypeScript.
🕗 Version & Regression Information
This is the behavior in every version I tried, and I reviewed the FAQ for entries about type inference.
Unfortunately this is the smallest snippet of code I could shrunk the issue to:
typeOption<A>=Some<A>|NoneinterfaceSome<A>{_tag: 'Some'value: A}interfaceNone{_tag: 'None'}constsome=<A,>(x: A): Option<A>=>({_tag: 'Some',value: x})constnone: Option<never>={_tag: 'None'}constisNone=<A,>(x: Option<A>): x is None=>x._tag==='None'// --------------------typeEither<E,A>=Left<E>|Right<A>interfaceLeft<E>{_tag: 'Left'left: E}interfaceRight<A>{_tag: 'Right'right: A}constleft=<E,A=never>(x: E): Either<E,A>=>({_tag: 'Left',left: x})constright=<A,E=never>(x: A): Either<E,A>=>({_tag: 'Right',right: x})constisLeft=<E,A>(a: Either<E,A>): a is Left<E>=>a._tag==='Left'// --------------------interfaceURItoKind1<A>{'Option': Option<A>}interfaceURItoKind2<E,A>{'Either': Either<E,A>}typeURIS1=keyofURItoKind1<any>typeURIS2=keyofURItoKind2<any,any>typeKind1<URIextendsURIS1,A>=URItoKind1<A>[URI]typeKind2<URIextendsURIS2,E,A>=URItoKind2<E,A>[URI]typeHKT1<URI,A>={URI: URI;a: A};typeHKT2<URI,A,B>={URI: URI;a: A;b: B}interfaceFunctor1<FextendsURIS1>{readonlyURI: Fmap: <A,B>(f: (a: A)=>B)=>(fa: Kind1<F,A>)=>Kind1<F,B>}interfaceFunctor2<FextendsURIS2>{readonlyURI: Fmap: <E,A,B>(f: (a: A)=>B)=>(fa: Kind2<F,E,A>)=>Kind2<F,E,B>}interfaceFunctor<F>{readonlyURI: Fmap: <A,B>(f: (a: A)=>B)=>(fa: HKT1<F,A>)=>HKT1<F,B>}// --------------------------constoptionFunctor: Functor1<'Option'>={URI: 'Option',map: <A,B>(f: (x: A)=>B)=>(fa: Option<A>): Option<B>=>isNone(fa) ? none : some(f(fa.value))}consteitherFunctor: Functor2<'Either'>={URI: 'Either',map: <E,A,B>(f: (x: A)=>B)=>(fa: Either<E,A>): Either<E,B>=>isLeft(fa) ? fa : right(f(fa.right))}// ---------------------------typeCompose=<A,B,C>(f: (x: B)=>C,g: (x: A)=>B)=>(x: A)=>Cconstcompose: Compose=(f,g)=>x=>f(g(x))typeComposeR=<A,B,C>(g: (x: A)=>B,f: (x: B)=>C)=>(x: A)=>CconstcomposeR: ComposeR=(g,f)=>x=>f(g(x))// ---------------------------typeIncrement=(x: number)=>numberconstincrement: Increment=x=>x+1typeToStringg=(x: number)=>stringconsttoStringg: ToStringg=x=>`${x}`constcomposed=compose(toStringg,increment)constcomposedR=composeR(increment,toStringg)composed(12)// "13"composedR(12)// "13"// This section compiles ok and types inferred correctly when composing functions.// ---------------------------constmap1=optionFunctor.mapconstmap2=eitherFunctor.mapconstcomposed1=compose(map1,map2)// <=== map2 has error and types cannot be inferred correctlyconstcomposed2=composeR(map1,map2)// <=== map2 is ok here!// Try switching map1 and map2. Why in `composed1` TypeScript cannot infer types correctly? how can I fix it?
🙁 Actual behavior
Type inference on compose function is not working as expected. This is from the snippet and sample code above.
// ...constcomposed1=compose(map1,map2)// <=== map2 has error and types cannot be inferred correctlyconstcomposed2=composeR(map1,map2)// <=== map2 is ok here!
🙂 Expected behavior
In both ways of composing map functions (map1, map2), typescript could infer generic type arguments correctly.
Additional information about the issue
This code is heavily influenced by fp-ts library. (I've already investigated the library. The library has a flow function that is implemented similar to the compose function in the TypeScript docs here, but there are no function like my compose function there that parameters are swapped.
The way I implemented the compose function and choosing the arguments order is based on the Composition-Operator in Math and Haskell for when we try to compose functions: (f o g in Math and f . g in Haskell means call g with the input parameter first and then call f with the output of g, and return)
The text was updated successfully, but these errors were encountered:
🔎 Search Terms
It's been 2 years that I'm playing with higher order functions and kinds. This is part of my effort on creating a course of "Functional Programming with TypeScript". You can find the content here.
I'm working on a next video to teach "Functor composition", but I'm getting to a strange behavior on argument inference that I cannot reason about. You have an example of what I'm working on (
compose
function) in the docs here.What baffles me is why swapping place of arguments in the compose function, makes my examples for higher kinded types fail. I've explained this completely in a question with a sample snippet of code and playground in StackOverFlow here. I'm trying this with the latest version of TypeScript.
🕗 Version & Regression Information
⏯ Playground Link
Link
💻 Code
Unfortunately this is the smallest snippet of code I could shrunk the issue to:
🙁 Actual behavior
Type inference on compose function is not working as expected. This is from the snippet and sample code above.
🙂 Expected behavior
In both ways of composing
map
functions (map1
,map2
), typescript could infer generic type arguments correctly.Additional information about the issue
This code is heavily influenced by fp-ts library. (I've already investigated the library. The library has a
flow
function that is implemented similar to thecompose
function in the TypeScript docs here, but there are no function like mycompose
function there that parameters are swapped.The way I implemented the
compose
function and choosing the arguments order is based on the Composition-Operator in Math and Haskell for when we try to compose functions: (f o g
in Math andf . g
in Haskell means callg
with the input parameter first and then callf
with the output ofg
, and return)The text was updated successfully, but these errors were encountered: