3 ms·
One thing I really need is getting at "the type field". This is useful information by itself, and I want it most of the time - but I think in Haskell it's not p
by jstimpfle 9y ago
One thing I really need is getting at "the type field". This is useful information by itself, and I want it most of the time - but I think in Haskell it's not possible to get at it without manually defining a "type type" and a function that computes that type from a given sum-typed data.
I don't think that lambdas/closures have such a problem in most programming languages. It's still unclear how to handle function pointers for lambdas/closures, but it should be a lot easier to make a lambda into a real function than changing a datatype definition when it's already used across the codebase.
So, I think sum types are really another one of those ideas which may prevent some bugs sometimes, but in the end are not generally super useful, and are just a little too clever and end up causing a more work than they save. At least unless there's a well-defined representation from which we can extract the type information, in which case I'd probably be happy to use them.
- tom_mellior 9y agoCould you give an example in a (possibly hypothetical) programming language of your choice of what you mean by "the type field", and what you would do with it? Maybe we could point you to something that does what you want. Though I do wonder if reflection on type information (at runtime?) would be useful and easy for you if you find basic algebraic data types "a little too clever" and "causing more work than they save".
- jstimpfle 9y agoRegarding the type field, look for "enum EventType type;" in the article. (In practice I would not use "enum EventType", but simply "int" - but anyway). What I would do with it? Like the result of (x+y), or any type of data, I can use it for whatever I need. I might simply want to count the number of occurences of each "type". Often I'm simply not interested in the data besides the "type", and in these cases I find that case-matching is a lot of boilerplate. And it doesn't reify the "type" to authoritative runtime values. Which, as you say, might be done with reification support from the language, but I don't want to bother with that -- and it would not allow me to choose the possible values of the type field, which might be a requirement if the options are defined somewhere else.
- tom_mellior 9y ago> Regarding the type field, look for "enum EventType type;" in the article. OK. If I understand correctly, you want to use that field in other contexts than doing a switch on it, whereas in Haskell the usual way of consuming the type tag is to do such a switch, as in the article's example: case event of ClickEvent x y -> <do click stuff> PaintEvent color -> <do paint stuff> // there are no other possible cases, so no default case needed > For example, I could separate a large array of sum-type data into separate arrays, and also process them separately. Sure, but you can do that with a case expression as above. Here is a complete example: data Event = ClickEvent Int Int | PaintEvent Int -- separate a list of Events into two lists, the first holding the payloads -- of the click events, the other the payloads of the paint events separate :: [Event] -> ([(Int, Int)], [Int]) separate [] = ([], []) separate (event : events) = case event of ClickEvent x y -> ((x, y) : clicks, colors) PaintEvent color -> (clicks, color : colors) where (clicks, colors) = separate events example = [ClickEvent 1 2, PaintEvent 42, ClickEvent 3 4, PaintEvent 23] Running it: > separate example ([(1,2),(3,4)],[42,23]) You can now go ahead and process the list [(1,2),(3,4)] separately from [42,23]. (Depending on the exact specification and performance requirements, the code might be written a bit differently in the real world.) EDIT: Ah, you edited your post while I was writing. Oh well. Regarding reifying constructors as integers or whatever, yes, you might have to write a two-line function to do that.
- jstimpfle 9y agoYep, sorry for the edit. I did the arrays example but then noticed it was not really an answer to your question, since it can be done with sum types in almost the same way.
- jerf 9y ago"but I think in Haskell it's not possible to get at it without manually defining a "type type" and a function that computes that type from a given sum-typed data." You can do it with reflection, which somewhat confusingly Haskell calls "generics": https://wiki.haskell.org/GHC.Generics https://wiki.haskell.org/GHC.Generics It's very old terminology, you'll have to forgive them. If you carefully trace the etymology back to the 1990s-ish when Haskell established that term, and trace the academic ideas of "generics" back that far, you can actually find the similarities with the old academic ideas of generics and what we today call reflection, but nowadays they are so separated it's really just of historical interest. That said, very little code uses it, because generally in Haskell whenever you would want to do "OK, look at the type of a thing, then decide what to do based on the type, then do the thing" that almost always can be collapsed down to "Pattern match on the thing, then do the thing you wanted to do". You write what it is you want to do based on types into the pattern matching itself, collapsing the middle step where you have an explicit value representing the type into implicit knowledge contained in the branch of the pattern match that occurs. I do understand why you are thinking that way and am not trying to say it's a bad thing in general. While it's not the first thing I reach for in my Go code, I do have some very reflection-heavy Go code that works that way. But in Haskell, there's almost (but not quite) always a better solution for Haskell. If you could program the way you are discussing in Haskell, you'd find the type checker would be an even bigger jerk to you than it can be when you program the "right" way; I think you'd have a hard time making that code work with GADTs, and GADTs are very, very common in Haskell.