Haskell features I'd like to see in other languages
intoverflow.wordpress.com
intoverflow.wordpress.com
It seems that Haskell is the language I've been searching for all my life :-) ... unfortunately code written in it looks really scary.
I mean ... when I look at good Python / Ruby code, I just get what's going on ... that's partly because I know these languages, but on the other hand they encourage programmers to do literary story-telling.
Maybe someday Haskell will get reinvented into some form meant for mere mortals :)
> [...] but on the other hand they encourage programmers to do literary story-telling.
Try literate Haskell. (I say this only partially tongue-in-cheek.) Really, I think that Haskell is very geared towards being read by humans. This is mostly because it's so good at gluing, that you can break your problem apart in smaller pieces in the first place.
E.g. let's look at a program that computes sqrt(2). With Haskell you can talk first about how to produce infinite list of ever better approximations:
next x = (2 / x + x) / 2
approximations = iterate next 1
and then talk about when to stop and finally give an answer later on. goodEnough x = abs (2-x*x) <= 0.0001
sqrtTwo = find goodEnough approximations
In a strict language, you usually intermingle producing new values with watching out for the ending condition: guess = 1
while abs(2 - guess*guess) >= 0.0001:
guess = (2/guess + guess) / 2
Python's generators help, but they are more restricted. (Which is intended, Python wants to stay simple.) y = 2
average x1 x2 = (x1 + x2) / 2
next x = average (y/x + x)
It's just a co-incidence that I chose y as 2.[1] Heron's Method (http://en.wikipedia.org/wiki/Herons_method#Babylonian_method)
You will enjoy the Haskell Cafe later.
By the way, one small exercise (from a paper by Chris Okasaki): Write a function with the type
Tree a -> Tree Int
where data Tree a = Node a (Tree a) (Tree a) | Empty
your function should all nodes in a tree with their position in level order traversal (also called breadth first search). E.g. (lot == lever order traversal) lot Empty = Empty
lot (Node 'a' (Node 'b' Empty (Node 'c' Empty Empty))
(Node 'd' Empty Empty)) =
(Node '0' (Node '1' Empty (Node '3' Empty Empty))
(Node '2' Empty Empty))OCaml, that's a nice language, with much better introductary materials.
What still gets me are some of the basic functional programming concepts like monads and the y combinator, which obviously aren't unique to Haskell. Yeah, I've read Wikipedia on both of those (more than once). And several other things. I've even written some Lisp for my employer. But some of the concepts have yet to "click." I know monads encapsulate side effects or something (and the y combinator finds the fixed points of a function), but I feel like I just don't get the details of how it works.
After all, you don't need to know anything about monads to call the print function.
If you have worked with mathematical groups and have seen how mathematicians can look at very disparate things with their group-goggles and find similarities, you will appreciate monads.
The y combinator is a nice parlour trick (if you define it in lambda calculus without using any other recursion primitives), and research vehicle. But I did not apply it once, yet. And I've tried.
If you want to get combinatorial logic (including the y combinator), read "To Mock a Mockingbird". And do all the puzzles, of course. Actually you should read it, whether you want to learn anything about combinatorial logic or not. It is such a good book.
It's not just you. Lazy evaluation makes predicting and understanding resource usage difficult.
The guys who wrote XMonad got around it, so it is possible. You just need to be careful, and use enough strictness annotations.
One of the craziest things is that Haskell has decided that type families should also be lazily evaluated. So you can hop into the interactive interpreter to inspect some types and get not-so-useful information, like
somevar :: SomeTypeFunction (SomeTypeFunction2 Int) (SomeTypeFunction3 (SomeTypeFunction 3 (SomeTypeFunction3 String)))
which might actually evaluate to something simple like (String, Int), though you wouldn't know it without either tracing through the functions (yuck!) or writing some silly helper functions whose sole job is to force evaluation of the type functions (yuck!). I've never personally been thankful for this behavior.Chris Okasaki argues in "Purely Functional Data Structures" for making lazy evaluation optional, but easy to use. Some data structures have optimal asymptotics only when evaluated strictly and some only when evalutated lazily.
With better strictness analysers most of the problems lazyness is causing at the moment, may go away. A Haskell compiler is free to make your program (or parts of it) strict, if it can prove that this preserves semantics.
{-# LANGUAGE
TypeFamilies,
MultiParamTypeClasses,
FlexibleInstances #-}
data True = T
data False = F
class LogicOr a b where
type Or a b
typeOr :: a -> b -> Or a b
instance LogicOr True b where
type Or True b = True
typeOr T b = T
instance LogicOr a True where
type Or a True = True
typeOr b T = T
instance LogicOr False False where
type Or False False = False
typeOr F F = F
class Decide tf a b where
type If tf a b
nonFunctionalIf :: tf -> a -> b -> If tf a b
instance Decide True a b where
type If True a b = a
nonFunctionalIf T a b = a
instance Decide False a b where
type If False a b = b
nonFunctionalIf F a b = b
whatIsMyType tf1 tf2 a b = nonFunctionalIf (typeOr tf1 tf2) a b
If we load this into ghci, we have the following example: *Main> :t whatIsMyType F F 2 "foo"
whatIsMyType F F 2 "foo" :: (Num t) => If (Or False False) t [Char]
*Main> :t whatIsMyType F F 2 "foo" :: String
whatIsMyType F F 2 "foo" :: String :: String
In the first :t, ghci lazily (though correctly) gives us the type of whatIsMyType. In the second example, where we provide an explicit type annotation, ghci is able to (correctly) conclude that our annotation was correct by evaluating the various type functions it had given us before.That is the part you must avoid. In Haskell, you don't tell a story. You depict a landscape. (There is a story, in the IO monad, but it is minimized and segregated.)
Flip through the first few chapters of Real World Haskell online and see that it is fairly readable.
I love the idea of this article, talking about specific features in your language you'd like to see gain wider adoption. It's too bad I had trouble understand what was going on.
I read a blog post were somebody re-implemented some examples of Haskell concepts in C. I can't remember the URL, but searching for "Monads in C" or "Monads in Java" etc brings up some interesting posts. E.g. http://blog.tmorris.net/maybe-monad-in-java/
I'd very much appreciate suggestions for things to address in such a series! Any time I write something like this, I always learn a lot in the ensuing discussions. Mostly I learn technical things, but it's good to learn more about the writing process as well.
Firstly, I'm not sure what audience you had in mind when you wrote this. If it's Haskell programmer, then I think it's too detailed. If not, I think it's not detailed enough (but it does include all sorts of words/concepts a non-Haskell programmer won't understand).
I think a shorter article, focused on less features would have been better (or a series of articles). It feels to me like you could talk in length about each of these features, and the article is both too long because of so many features, but too short in each specific feature.
Finally and most importantly, what you're really missing is code examples in other languages. You mentioned doing this, and I agree 100%. That would make your article much easier for someone like me. Although I vote the examples be in Python :)
I'm looking forward to your next post!
(I like Python more in general, but I am not sure, if you can demonstrate some of the static goodness of Haskell-analogies in it. Polymorphism on return types is awesome, but not real imaginable in Python, but barely imaginable in Java.)
Depending on how much autonomy you have at work, Haskell is a reasonable language for getting "real work" done. The technology is ready to do the job: as with any language selection discussion, the issue is more about workplace culture (maintenance plans, hiring, etc).
In terms of advanced types, my understanding is that F# is playing catch-up to Haskell, but that's not really a slight against F#. I happen to prefer Haskell, but it's not obvious to me that a person who knows both languages would agree with me.
So the answer your question, or more precisely to not answer it, I think you'll find either language rewarding. If you think F# might be more useful at work, that's definitely worth considering. If you want to see the state of the art in (well supported) statically typed functional programming, Haskell is a strong contender. Regardless of which you decide to look at, the skills will translate fairly well if you later go to look at the other.