What's the closest thing to Haskell GADTs and

2020-02-26 09:47发布

问题:

F# is an ML with OOP. What's the closest it comes to Haskell generalized algebraic data types and typeclasses?

回答1:

The answer depends on what problem are you trying to solve. F# does not have typeclasses and GADTs, so there is no direct mapping. However, F# has various mechanisms that you would use to solve problems that you typically solve in Haskell using GADTs and typeclasses:

  • If you want to represent object structures and be able to add new concrete implementations with different behaviour, then you can often use standard OO and interfaces.

  • If you want to write generic numeric code, you can use static member constraints (here is an example), which is probably technically the closest mechanism to type classes.

  • If you want to write more advanced generic code (like universal printer or parser) then you can often use the powerful F# runtime reflection capabilities.

  • If you need to parameterize code by a set of functions (that perform various sub-operations required by the code) then you can pass around an implementation of an interface as @pad shows.

There is also a way to emulate Haskell type classes in F#, but this is usually not an idiomatic F# solution, because the F# programming style differs from the Haskell style in a number of ways. One fairly standard use of this is defining overloaded operators though (see this SO answer).

At the meta-level, asking what is an equivalent to a feature X in aother language often leads to a confused discussion, because X might be used to solve problems A, B, C in one language while another language may provide different features to solve the same problems (or some of the problems may not exist at all).



回答2:

In F#, you often use interfaces and inheritance for these purposes.

For examples' sake, here is a simple typeclass using interfaces and object expressions:

/// Typeclass
type MathOps<'T> =
    abstract member Add : 'T -> 'T -> 'T
    abstract member Mul : 'T -> 'T -> 'T

/// An instance for int
let mathInt = 
    { new MathOps<int> with
       member __.Add x y = x + y
       member __.Mul x y = x * y }

/// An instance for float
let mathFloat = 
    { new MathOps<float> with
       member __.Add x y = x + y
       member __.Mul x y = x * y }

let XtimesYplusZ (ops: MathOps<'T>) x y z =
    ops.Add (ops.Mul x y) z

printfn "%d" (XtimesYplusZ mathInt 3 4 1)
printfn "%f" (XtimesYplusZ mathFloat 3.0 4.0 1.0)

It may not look very beautiful, but it's F#-ish way to do it. For a more Haskell-like solution which uses a dictionary-of-operations, you can have a look at this nice answer.