haskell programmer. using F#. no typeclasses in F#. what to use when I need typeclasses?
Asked
Active
Viewed 1,904 times
12
-
2I'm not sure if this is a duplicate, but at least the answers to this question are highly relevant: http://stackoverflow.com/questions/501069/f-functions-with-generic-parameter-types – Chuck Sep 08 '10 at 02:40
-
3Can you give a concrete example of when you "need typeclasses"? – J D Sep 08 '10 at 11:50
-
14@Jon Harrop: Can you give a concrete example of when you "need" any language feature? In the end, once you have Turing-equivalence, everything else is just syntax and convenience. Type classes are the most expressive approach to ad-hoc polymorphism I'm familiar with. Do you think subtype polymorphism and member overloading are useful? – C. A. McCann Sep 08 '10 at 13:47
-
@Camccann: So we agree the question is nonsense. Also, C++ has the "most expressive" type system I am familiar with, yet it sucks. If anyone thinks typeclasses can be useful, I'd still like to see a concrete example of that. – J D Sep 08 '10 at 17:47
-
1@Camccann: For example, perhaps the OP can describe the actual problem he is trying to solve rather than ask how to translate a solution that was written using an esoteric language feature? – J D Sep 08 '10 at 17:57
-
I had a similar problem in Java: http://stackoverflow.com/questions/3397160/how-can-i-pass-a-class-as-parameter-and-return-a-generic-collection-in-java – Jonas Sep 08 '10 at 18:22
-
3@Jon Harrop: So I take it you don't see any use for, say, operator overloading or type parameter constraints? – C. A. McCann Sep 08 '10 at 19:07
-
@Jonas: You can solve that problem with `Seq.choose (box >> function :? string as x -> Some x | _ -> None)`. – J D Sep 08 '10 at 19:16
-
@camcann: What led you to that conclusion? – J D Sep 08 '10 at 19:18
-
2@Jon Harrop: Do you think it's useful to constrain a type parameter based on whether a valid operator overload exists for the type? How about a "static interface" constraint that requires certain static methods or constructor signatures for the type? – C. A. McCann Sep 08 '10 at 19:42
-
@camccann: I don't believe I have ever found myself wanting to do either of those things. I have written many functions that are inferred to have constraints on operator overloads over type parameters but I have never written them manually in production code and the applications for that (factoring numerical code over different numerical types) require predictably-good performance that, AFAIK, type classes cannot provide and require other custom functionality to be passed explicitly anyway. – J D Sep 08 '10 at 21:14
-
@camccann: So, assuming you have operator overloading, type classes only seem to provide a slight advantage in some very obscure situations. I cannot imagine why anyone would say they "need" them and I am baffled as to why Brian McNamara from the F# team at Microsoft is bashing F# for not having this obscure language feature when the only example he has come up with sucks donkey brains through a straw. If a better example exists, I'd love to see it... – J D Sep 08 '10 at 21:17
-
4@Jon Harrop: Well, if you've specialized in high-performance numerical computation I can definitely see why it might not be relevant to your day-to-day work, but that's a pretty small niche with unique requirements. On the other hand I expect that @Brian, being on the F# team, has an extremely broad perspective on how the language might be used. – C. A. McCann Sep 08 '10 at 21:31
-
@camccann: I am not specialized in high-performance numerical computation. I said that was the only context in which the feature you referred to has been useful to me. So your feature is in the "small niche", not me. If Brian is as capable as you expect then he should be able to provide a much more compelling example. – J D Sep 08 '10 at 23:16
-
4Dictionary passing is one implementation of type-classes, it's not the only way. JHC compiler is one example of a (whole-program optimizing) Haskell compiler which does NOT use dictionary passing to implement type-classes. – snk_kid Sep 09 '10 at 08:51
-
1@snk_kid: Do you really expect .NET to do that level of whole program optimization? How many useful features would you have to sacrifice just to make it theoretically possible? How many orders of magnitude slower would (run-time!) compilation be? – J D Sep 09 '10 at 09:33
1 Answers
20
Do check out this as someone suggested.
I think the short answer is to pass dictionaries-of-operations (as Haskell would under the hood; the witness for the instance).
Or change the design so you don't need typeclasses. (This always feels painful, since typeclasses are the best thing ever and it's hard to leave them behind, but before Haskell and typeclasses came along, people still managed to program for 4 decades previously somehow without typeclasses, so do the same thing those folks did.)
You can also get a little ways with inline
static member constraints, but that gets ugly quickly.
Here's a dictionary-of-operations example:
// type class
type MathOps<'t> = { add : 't -> 't -> 't; mul: 't -> 't -> 't } //'
// instance
let mathInt : MathOps<int> = { add = (+); mul = (*) }
// instance
let mathFloat : MathOps<float> = { add = (+); mul = (*) }
// use of typeclass (normally ops would the 'constraint' to the left of
// the '=>' in Haskell, but now it is an actual parameter)
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)
-
@Brian: "typeclasses are the best thing ever". I'd love see an example where typeclasses solve a real problem that wasn't already better solved by another technique. Also, your example retrofits the dictionary of operations rather than defining them with the original type definition. Is that possible with type classes? – J D Sep 08 '10 at 12:06
-
14@Jon Harrop: What? Of course it is. Defining type classes and instances is completely separate from defining types. They don't even have to be in the same module. If memory serves me, Scala's version is even more powerful and lets you have locally scoped definitions (but I don't know Scala, so I may be confused). – C. A. McCann Sep 08 '10 at 13:36
-
@camccann: Ah yes, of course. That's why you cannot rely upon them being statically resolved so your performance might be that of boxing and dispatch instead of an MLA instruction, unpredictably degrading performance by over an order of magnitude as it does in Brian's example? – J D Sep 08 '10 at 17:53
-
2@camccann For the record, yes, Scala let you locally scope definitions. It is more awkward to use, however, and there's little library use of type classes at the moment. Hopefully, both things will get fixed in the future. – Daniel C. Sobral Sep 08 '10 at 19:58
-
1@Daniel: Thanks! I remembered reading something about it and thinking it sounded useful, particularly for dealing with cases like Haskell's `Monoid` where e.g. `Float` has no less than four obvious and useful instances. One of these days I'll get around to learning Scala... – C. A. McCann Sep 08 '10 at 20:09
-
-
2Dictionary passing is one implementation of type-classes, it's not the only way. JHC compiler is one example of a (whole-program optimizing) Haskell compiler which does NOT use dictionary passing to implement type-classes. – snk_kid Sep 09 '10 at 08:52
-
6There seems to be another problem which hasn't been mentioned, it's not just the lack of type-classes. The fact that F# (because of .NET) has no support for higher-kinded polymorphism which means you can not (trivially) implement some of the abstractions possible in languages like Haskell, even with explicit dictionary passing you can't do it. I really hope this is sorted out in .NET/F# in the future. – snk_kid Sep 09 '10 at 08:55
-
@snk_kid: Can you give a compelling example where higher kinded polymorphism is useful? – J D Sep 09 '10 at 09:27
-
-
3@Jon Harrop: So you're not a fan of computation expressions in F#, I take it? – C. A. McCann Sep 10 '10 at 02:38
-
4@Jon of course it is. Monads aren't just for side-effects you know. – Simon Marlow Sep 10 '10 at 09:26
-
-
@Simon Marlow: what else would monad overloading be useful for outside pure languages? – J D Sep 10 '10 at 14:00
-
1@Jon: The list monad (or other more optimized monads representing nondeterminism) is the most obvious example of a monad which cannot easily be embedded in the "default monad" of an impure language without multi-shot continuations. As soon as you want to define basic "control flow" operations like `mapM`, `filterM`, `sequence` that can operate on either nondeterministic computations or ordinary side-effecting computations, you need to be able to abstract over higher-kinded type constructors. – Reid Barton Sep 11 '10 at 20:06
-
@Reid Barton: Do you really want to be able to define control flow constructs such that the same one can run either nondeterministic computations or ordinary side-effecting computations? This sounds exactly like the use of OOP to abstract over kinds of collections that never caught on in OCaml... – J D Sep 11 '10 at 20:56
-
3Sure. I regularly use `replicateM` for things like either reading a list of integers from standard input (`replicateM 5 (readLn :: IO Integer)`) or generating all possible strings of booleans of a given length (`replicateM 5 [False, True]`). If in the latter case I later decide to replace the list monad with a more efficient nondeterminism monad, I don't want to have to rewrite `replicateM`. – Reid Barton Sep 11 '10 at 21:54
-
1(As for abstracting over kinds of collections, that's never really caught on in Haskell either, not that people haven't tried. It seems that the similarities between collections are outweighed by the little differences between them, such as whether the elements have an intrinsic ordering, whether there are values associated with keys, etc., so that there is not a canonical "best" set of operations on a collection. In contrast the monad interface is very simple and well-defined with solid theoretical underpinnings.) – Reid Barton Sep 11 '10 at 22:05
-
1
-
1Give up guys, Jon is Jon. He's always right, everywhere, every time. :-) He won't stop arguing until you accept that. – soc Sep 26 '10 at 23:25