views:

1522

answers:

5

Coming from C++, I find generic programming indispensable. I wonder how people approach that in Haskell?

Say how do write generic swap function in Haskell?

Is there an equivalent concept of partial specialization in Haskell?

In C++, I can partially specialize the generic swap function with a special one for a generic map/hash_map container that has a special swap method for O(1) container swap. How do you do that in Haskell or what's the canonical example of generic programming in Haskell?

+15  A: 

This is closely related to your other question about Haskell and quicksort. I think you probably need to read at least the introduction of a book about Haskell. It sounds as if you haven't yet grasped the key point about it which is that it bans you from modifying the values of existing variables.

Swap (as understood and used in C++) is, by its very nature, all about modifying existing values. It's so we can use a name to refer to a container, and replace that container with completely different contents, and specialize that operation to be fast (and exception-free) for specific containers, allowing us to implement a modify-and-publish approach (crucial for writing exception-safe code or attempting to write lock-free code).

You can write a generic swap in Haskell, but it would probably take a pair of values and return a new pair containing the same values with their positions reversed, or something like that. Not really the same thing, and not having the same uses. It wouldn't make any sense to try and specialise it for a map by digging inside that map and swapping its individual member variables, because you're just not allowed to do things like that in Haskell (you can do the specialization, but not the modifying of variables).

Suppose we wanted to "measure" a list in Haskell:

measure :: [a] -> Integer

That's a type declaration. It means that the function measure takes a list of anything (a is a generic type parameter because it starts with a lowercase letter) and returns an Integer. So this works for a list of any element type - it's what would be called a function template in C++, or a polymorphic function in Haskell (not the same as a polymorphic class in C++).

We can now define that by providing specializations for each interesting case:

measure [] = 0

i.e. measure the empty list and you get zero.

Here's a very general definition that covers all other cases:

measure (h:r) = 1 + measure r

The bit in parentheses on the LHS is a pattern. It means: take a list, break off the head and call it h, call the remaining part r. Those names are then parameters we can use. This will match any list with at least one item on it.

If you've tried template metaprogramming in C++ this will all be old hat to you, because it involves exactly the same style - recursion to do loops, specialization to make the recursion terminate. Except that in Haskell it works at runtime (specialization of the function for particular values or patterns of values).

Daniel Earwicker
swap is the canonical c++ gp example. You can try to illustrate the same concept with a canonical Haskell example as well. I've revised the question to reflect that.
obecalp
+6  A: 

As Earwicker sais, the example is not as meaningful in Haskell. If you absolutely want to have it anyway, here is something similar (swapping the two parts of a pair), c&p from an interactive session:

GHCi, version 6.8.2: http://www.haskell.org/ghc/  :? for help
Loading package base ... linking ... done.
Prelude> let swap (a,b) = (b,a)
Prelude> swap("hello", "world")
("world","hello")
Prelude> swap(1,2)
(2,1)
Prelude> swap("hello",2)
(2,"hello")
TheMarko
Pleas keep in mind, that this doesn't actually swap the values but returns a new pair (since the original pair is immutable).
TheMarko
It's very similar to what ocaml does. What if I want to swap two hash tables or map, how do you ensure the values are not copied?
obecalp
If you want to operate on big tables of state you probably don't want to be using functional programming in the first place. If you absolutely need to, there's IORef and the state monad to perform calculations which maintain a state.
TheMarko
Well, the question I'm asking is about "partial specialization" in the sense that the feature can support alternative implementations for different types for the same interface.
ididak
@obecalp: you can create a new Map where two values are swapped. This new Map will share most of the old one's structure, so it's not an O(N) copy operation, just O(log N).
yairchu
@obecalp: what do you mean swap 2 maps? you'd just do `swap (map1, map2)`, where `map1` and `map2` are variables containing the maps. values won't be copied, you're just returning the same values in a different order.
Claudiu
+2  A: 

After reading enough in a Haskell book to really understand Earwicker's answer I'd suggest you also read about type classes. I'm not sure what “partial specialization” means, but it sounds like they could come close.

Magnus
+6  A: 

In Haskell, functions are as generic (polymorphic) as possible - the compiler will infer the "Most general type". For example, TheMarko's example swap is polymorphic by default in the absence of a type signature:

*Main> let swap (a,b) = (b,a)
*Main> :t swap
swap :: (t, t1) -> (t1, t)

As for partial specialization, ghc has a non-98 extension:
file:///C:/ghc/ghc-6.10.1/doc/users_guide/pragmas.html#specialize-pragma

Also, note that there's a mismatch in terminology. What's called generic in c++, Java, and C# is called polymorphic in Haskell. "Generic" in Haskell usually means polytypic: http://haskell.readscheme.org/generic.html
But, aboe i use the c++ meaning of generic.

ja
+5  A: 

In Haskell you would create type classes. Type classes are not like classes in OO languages. Take the Numeric type class It says that anything that is an instance of the class can perform certain operations(+ - * /) so Integer is a member of Numeric and provides implementations of the functions necessary to be considered Numeric and can be used anywhere a Numeric is expected.

Say you want to be able to foo Ints and Strings. Then you would declare Int and String to be instances of the type class Foo. Now anywhere you see the type (Foo a) you can now use Int or String.

The reason why you can't add ints and floats directly is because add has the type (Numeric a) a -> a -> a a is a type variable and just like regular variables it can only be bound once so as soon as you bind it to Int every a in the list must be Int.

stonemetal