Llewellyn Frost Haskell (born Thomas Frost Haskell; October 8, 1842 - November 26, 1929) was a Union Army officer during the American Civil War
⢠Other Related Knowledge ofhaskell
â â â â â â
Henry L. Haskell
Henry Lincoln Haskell (January 2, 1863 - April 3, 1940) was a businessman and inventor. He ran manufacturing businesses related to plywood and lived in Ludington, Michigan, US
â â â â â â
What are 20 lines of code that capture the essence of Haskell? Try to follow standard Haskell style. Comments and spaces between code do not count as lines. They can also consist of individual snippets of code, but have to add up to 20 lines.
I have two examples, both related to math and working towards symbolic differentiation. They're based on a couple of blog posts and a paper (all referenced in the answer).People sometimes complain that Haskell examples tend to be too mathy, and it's somewhat true here. I had some non-mathy examples in mind as well (like using lenses to work with JSON or HTML), but just elaborating about differentiation led to a sufficiently long answer-and took long enough to write-that I decided to drop those.Automatic DifferentiationnThe general concept of automatic differentiation has been around for a while. The idea is that we can efficiently compute the value of the derivative of a function at a point by creating a numeric type that carries around both its value and its derivative, defining arithmetic functions appropriately. Note that this is not the same as symbolic or numeric differentiation, and has significant advantages over both. It's simpler, faster and more general than symbolic differentiation while being more accurate and flexible than numeric differentiation.The classic way to do this is to define a pair of numbers (x, x') where x is the current value and x' is the value of the derivative. However, this only gets us the first derivative of our function-what if we want more? Happily, since Haskell is lazy, we can define the type to carry all of its derivatives and only calculate the ones we need. At each step, we're working with an infinite tower of derivatives.I originally encountered this in Conal Elliott's Beautiful Differentiation blog post, but it originates in 'Functional Differentiation of Computer Programs" by Jerzy Karczmarczuk, presented at ICFP '98. Perhaps it'll make more sense with code:-- | The value of a function at a point with all of its derivatives:data DX a = DX val :: a, dx :: DX a Every derivative of a constant is 0. Overloading fromInteger in the Num class lets us use numeric literals for our DX type.instance Num n => Num (DX n) where fromInteger x = DX (fromInteger x) 0If you're not familiar with Haskell record syntax, DX (fromInteger x) 0 is a short way to write DX val = fromInteger x, dx = 0. Note one subtlety: the 0 in dx = 0 is of type DX n itself, which is how this turns into an infinite tower of derivatives. It might not look it, but that definition is circular¹.Now we can implement our basic numeric operations (also from the Num class). well, why not? I guess? The somewhat arbitrary numeric class hierarchy in Haskell is a sore point with some people.signum gives us 1 if the number is positive, 0 if it's 0 and -1 if it's negative. We'll just ignore the discontinuity at 0 and treat it like a constant function:signum (DX xâ x') = DX (signum xâ) 0abs is absolute value and, handily,-is all valid Haskell. You don't have to do it this way, but it has style.While we're at it, let's also define the Fractional instance because we want to be able to work with fractions and divide. let's add Eq and Ord instances:instance Eq a => Eq (DX a) where a == b = val a == val binstance Ord a => Ord (DX a) where compare a b = compare (val a) (val b)We can make both of them a bit prettier using on from Data. Function:instance Eq a => Eq (DX a) where (==) = (==) on valinstance Ord a => Ord (DX a) where compare = compare on valTo put all this together, we need to add a rule for handling the variable of differentiation. What's the derivative of x in f(x) = x? It's 1.var x = DX x 1To make life a bit easier, let's print the value, first derivative and second derivative at a point:instance Show a => Show (DX a) where show (DX x (DX x' (DX x'' _))) = show [x, x', x'']Now we can play around. Lets find the value and derivatives of 7x^2 3x 2 at x = 5:λ> ( x -> 7*x^2 3*x 2) (var 5)192, 73, 14Note how we got exponentiation (^) for free: it's defined in terms of *, which is correctly overloaded for our DX type. (Note that this only works for constant powers: in x ^ n, n is an int and it just translates to x * x * . * x n times.)Try it with some more complex examples. One neat thing is that arbitrary control flow and logic should all work correctly, including things like ifs. We didn't actually need to specify abs manually (we just did it because it was part of the Num class):abs' x = if x sqr (var 9)[3.0,0.16666666666666666,-9.259259259259259e-3]So there we have it: the first few lessons of Calc I in executable form, in less than 20 lines of Haskell.We can make this more general by defining more functions (trigonometric functions, logarithms. etc) as well as higher-order operations like the chain rule for composing functions. (The chain rule is particularly useful because it makes defining many of the other functions much easier.)One cool thing is that this works with any reasonable numeric type. We can use floating point numbers, rational numbers, complex numbers or even symbolic numbers (assuming we implement the core operations symbolically for them). Symbolic NumbersnIn fact, that final note got me thinking: symbolic numbers (which can be simplified as much as possible) are another great 20 line example of Haskell. It not only fits well with the differentiation code above but also demonstrates the power of defining and matching on algebraic data types.This code is a bit uglier than usual to fit into 20 lines, but it's for a good cause:data Op = Plus | Minus | Times | Divide deriving (Show, Eq)data Sym = Lit Double | Var String | Expr Op Sym Sym deriving (Show, Eq)instance Num Sym where fromInteger = Lit . fromInteger () = Expr Plus (-) = Expr Minus (*) = Expr Timesinstance Fractional Sym where fromRational = Lit . fromRational (/) = Expr Dividestep = case Expr op (Lit nâ) (Lit nâ) -> Lit $ (case op of Plus -> (); Minus -> (-); Times -> (*); Divide -> (/)) nâ nâ Expr op eâ eâ -> Expr op (step eâ) (step eâ) atom -> atomsimplify = until . iterate step where until (xâ : xâ : xs) | xâ == xâ = xâ | otherwise = until (xâ : xs)The cool thing here, of course, is how we can combine it with the automatic differentiation from the previous section:λ> let res = ( x -> 7*x^2 3*x 2) (var (Var "x"))λ> val (dx res)Expr Plus (Expr Plus (Expr Plus (Expr Times (Lit 7. 0) (Expr Plus (Expr Times (Var "x") (Lit 1.0)) (Expr Times (Var "x") (Lit 1.0)))) (Expr Times (Expr Times (Var "x") (Var "x")) (Lit 0.0))) (Expr Plus (Expr Times (Lit 3.0) (Lit 1.0)) (Expr Times (Var "x") (Lit 0.0)))) (Lit 0.0)Well, okay, our 20 lines didn't include room for a pretty printer, so we get an unreadable mess. Cheating a bit and writing a better show function lets us verify that this is correct:λ> let res = ( x -> 7*x^2 3*x 2) (var (Var "x"))λ> simplify (val (dx res))((((7.0 * ((x * 1.0) (x * 1.0))) ((x * x) * 0.0)) (3.0 (x * 0.0))) 0.0)Still ugly, but that's because my simplify function was not very sophisticated. If you work this out, though, it did indeed get the right derivative-symbolically. Adding a few simple cases to step (to handle multiplying by 0 and 1 and adding 0) cleans it up even more:((7.0 * (x x)) 3.0)So what have we done with these two examples? We've taught Haskell to differentiate functions in 20 lines of code, then we taught it simple symbolic arithmetic in 20 more lines of code-and we got simple symbolic differentiation for free.That's pretty cool.footnotesn¹ In case it's still a bit confusing-the notation is pretty terse-here's a more explicit way to say the same thing:zero = DX 0 zerofromInteger n = DX n zeroThe reason the other version works is because numeric literals are overloaded, so there's an implicit fromInteger in there:fromInteger n = DX n (fromInteger 0)I think the original version is significantly nicer than both of these, but they do make the circularity of the definition more evident. What are 20 lines of code that capture the essence of Haskell? Try to follow standard Haskell style. Comments and spaces between code do not count as lines. They can also consist of individual snippets of code, but have to add up to 20 lines. What are 20 lines of code that capture the essence of Haskell?