Flow: Generic type constraint ignored in higher-order function

Created on 24 Apr 2018  路  7Comments  路  Source: facebook/flow

Found a question on StackOverflow: https://stackoverflow.com/questions/49993018/flow-annotations-for-recursive-function/50002456

猸愶笍 How we'd like to write it using flow's generics

const Box = <A>(x:A): Box<A> => {
  return {
    map: <B>(f: A => B): Box<B> => Box(f(x)),
    fold: <B>(f: A => B): B => f(x),
  }
}

馃 Now we create a demo number => string function with a sample Box<string>

const numberToString = (x: number) : string =>
  String (x)

const b : Box<string> =
  Box("1")

鈿狅笍 This should fail, but it doesn't

b.map(numberToString)

馃攳 Query is:issue is:open generic has 150+ open issues. If this is a duplicate or isn't considered a bug, feel free to close this issue.

馃悰 View the bug here on flow.org/try

Most helpful comment

@naomik @mrkev here is an alternative without classes. It is more verbose than the class version see flow try link here but I think the outcome is the same.

Main issue I spotted with the original post was the overloaded use of Box as a value and as an undeclared type (which probably means it becomes any hence no errors?).

type Box<A> = {
  map: <B>(f: A => B) => Box<B>,
  fold: <B>(f: A => B)=> B
}

const box = <A>(x:A): Box<A> => {
  return {
    map: <B>(f: A => B): Box<B> => box(f(x)),
    fold: <B>(f: A => B): B => f(x),
  }
}

const numberToString = (x: number) : string =>
  String (x)

const stringToNumber = (x: string): number => +x;

const b : Box<string> = box("1")

// should fail
b.map(numberToString)
//shoult not fail
const k = b.map(stringToNumber)

All 7 comments

Could you please provide a link to flow.org/try?

@apsavin i updated the post

Hmm, this is unfortunate.
I'd suggest using classes as a workaround for now https://flow.org/try/#0MYGwhgzhAEBCD2APAPAQQHzQN4ChrUQC5pU9ph4A7CAFwCcBXYG+OgCiJIEptoaALAJYQAdImgBeAtAC+ZALZgADsljo2AM2KpJmWF2IIUa3nQCmNBnUrRKZgO5wkmtgOFiuPOfg3wQAE1V1LRJdOAM4UwsrGw1XIVFELxw5HApqGlsGeQAjMzoAFXgAZXpBSgBzSWgOYkpsvLoeYlo6cqqJdDJStsqapJw0qlpyaEMkZFb2zCk7RyM2ACIARkWuNJFFJTZ63Pyinvb1oA

@naomik @mrkev here is an alternative without classes. It is more verbose than the class version see flow try link here but I think the outcome is the same.

Main issue I spotted with the original post was the overloaded use of Box as a value and as an undeclared type (which probably means it becomes any hence no errors?).

type Box<A> = {
  map: <B>(f: A => B) => Box<B>,
  fold: <B>(f: A => B)=> B
}

const box = <A>(x:A): Box<A> => {
  return {
    map: <B>(f: A => B): Box<B> => box(f(x)),
    fold: <B>(f: A => B): B => f(x),
  }
}

const numberToString = (x: number) : string =>
  String (x)

const stringToNumber = (x: string): number => +x;

const b : Box<string> = box("1")

// should fail
b.map(numberToString)
//shoult not fail
const k = b.map(stringToNumber)

I think I like that one better since it keeps more of the original semantics 馃憤

@thehogfather That what I was looking for in the original question. Your solution looks really cool 馃憤

@thehogfather thanks for the explanation, I didn't realize that type name can conflict with variable names in flow. It is indeed verbose but does exactly what is needed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cubika picture cubika  路  3Comments

mjj2000 picture mjj2000  路  3Comments

jamiebuilds picture jamiebuilds  路  3Comments

glenjamin picture glenjamin  路  3Comments

mmollaverdi picture mmollaverdi  路  3Comments