Matching checklists using Haskell
by Porges
Our target for this exercise is “Things that other languages should take from Lisp”.
Bignum support
In Scheme and Common Lisp, by default you can’t overflow an integer…
Prelude> fac n = product [2..n] Prelude> fac 100 933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582 51185210916864000000000000000000000000 |
In Common Lisp, you can force your code to use fixed-size numbers (fixnums) for efficiency…
Prelude> let fac n = product [2..n] :: Int Prelude> fac 17 -288522240 |
Ruby and Python, by default, treat language-integers as logical-integers. Java, C, C++, and Perl don’t.
Prelude> :t 15 15 :: (Num t) => t |
Optional type declarations
[Common Lisp] allows, but does not require, type declarations.
Prelude> let doubleApply f x = f (f x) Prelude> :t doubleApply doubleApply :: (t -> t) -> t -> t |
The compiler can also use type declarations to perform compile-time typechecking. (Sadly, it isn’t required to do this.)
Prelude> doubleApply 3 doubleApply <interactive>:1:12: No instance for (Num (((t -> t) -> t -> t) -> (t -> t) -> t -> t)) arising from the literal `3' at <interactive>:1:12 |
Tail recursion
Proper (unbounded) tail-recursion (Scheme.)
Prelude> let loop = do { putStr "."; loop } Prelude> loop ...................................... ...................................... [etc] ...................................... ..........................Interrupted. |
(But see the Haskell wiki. Laziness introduces a small trick here.)
Functions which depend upon the type of multiple arguments
Methods with multiple-dispatch (Common Lisp.)
(This doesn’t exactly match conceptually because Haskell doesn’t have nominal overloading.)
multi Nothing Nothing = 0 multi (Just x) (Just y) = x + y multi Nothing (Just y) = y multi (Just x) Nothing = x |
Fast implementations
Some Lisp implementations are fast.
Haskell #7 on the Great Language Shootout (in terms of speed), and getting faster by the day as the backend of GHC is rewritten.
Syntactic Simplicity
Syntactic simplicity (Scheme.)
Or perhaps Python is another incarnation of syntactic simplicity.
Multiple dispatch is better represented by type classes, I think. It’s less flexible than mechanisms in other languages, but with some of GHC’s extensions, it’s not so bad. (Now if only it was easier to overload + and *!)
Haskell’s syntax is really bad for beginners. It’s just so subtle, it’s easy to make silly mistakes which boil down to an indentation a space or two too little. (It’s awesome once you learn the ins and outs, especially do-notation!)
Regarding multiple-dispatch, you can simulate that properly, with actual distinct types, using multiparameter typeclasses. In fact, you can go a little further, and make the code which is used depend on the result type as well (like read), or if necessary, write things which are either functions or non-function values depending on context.
Haskell’s optional type declarations aren’t really comparable to what Common Lisp does — the Haskell way is definitely more solid, but it will not allow you to write programs whose type correctness it can not prove, whereas CL allows you to do whatever you feel like.
I think the multiple dispatch idea would be more analogous to defining type class instances for more than one type at a time — or a tuple of types. Does Haskell allow such a thing?
“Methods with multiple-dispatch (Common Lisp.)”
Might I suggest multi-parameter type classes?
Ah, I didn’t see the comments already here…
Marijn:
Indeed Haskell (at least as implemented in GHC and Hugs) does support typeclasses with arbitrarily many type parameters. You can additionally require functional dependencies between them (such that the type of one parameter determines another uniquely). However, you can arguably get multiple dispatch even with a single-parameter typeclass, as the class can define a method whose type is just the type parameter of the class, and then the instances can define it at whatever types they’d like. Without lots of type signatures, this tends to lead to ambiguous types more quickly than other techniques though.
>Ruby and Python, by default, treat language-integers as logical-integers
Logical? Maybe mathematical?