April 2011

*
These are my original notes, slightly edited to make a coherent story
out of them, from when I was learning the basics of Lucid, in which
I try to map Lucid code as faithfully as possible onto Haskell.
*

I have recently spent some days learning Lucid, trying out examples, mostly from the pLucid manual, and then translating them into Haskell. I specifically did not want to write a Lucid interpreter in Haskell, but to explore the correspondences between the languages.

My first big revelation was when I tried this Lucid program:y where x = 0 fby x+1; y = 0 fby 0 fby 0 fby 0 fby 0 fby next x; endThe output starts with five zeros, and then 1, 2, 3 etc. I had previously supposed that x and y would have been synchronized, so that y would start with five zeros, and then 5, 6, 7 etc. Which would have been harder to map onto Haskell, but actually

next x = tail x prev x = head x : xI did read in an old paper that in older versions of Lucid one did not define:

ints = 0 fby ints+1but:

first ints = 0 next ints = ints+1so in those days streams were actually guaranteed to be synchronous, because there could be no "extra firsts". It is also easy to define

x `fby` y = head x : yAnd single values in Lucid correspond to an endless list of that value in Haskell: e.g.

ints = (repeat 0) `fby` (zipWith (+) ints (repeat 1))but a

ints = 0 : map (+1) intsSo you will probably not use the

xss@(x:xs) `upon` ~(y:ys) = x : (if y then xs else xss) `upon` ys(My first definition of

merge where merge = if a<b then a else b fi; a = xx upon a eq merge; b = yy upon b eq merge; xx = 2**i; yy = 3**i; i = 1 fby i+1; endIn Haskell this gives (with

main = print merge where merge = zipWith (\a b -> if a<b then a else b) a b a = xx `upon` zipWith (==) a merge b = yy `upon` zipWith (==) b merge xx = map (2^) i yy = map (3^) i i = 1 : map (+1) iI would dare say that the structure of the original is kept intact very nicely.

There are of course practical problems in converting code, for instance because Haskell is statically typed and Lucid is not, and also because IO works automagically in Lucid but has to be done explicitly in Haskell. An example with IO, being a translation of this example from the pLucid manual:

[% "smallest", s , "largest", h %] where s = x fby if next x < s then next x else s fi; h = x fby if next x > h then next x else h fi; end(Each time the user enters a number, the highest and the lowest number up till here are shown.)

import Data.List import Data.Maybe parse = unfoldr (listToMaybe . reads) :: String -> [Integer] unparse = concatMap (\(s1, i1, s2, i2) -> s1 ++ show i1 ++ s2 ++ show i2 ++ "\n") main = interact $ \str -> let x = parse str s = head x : zipWith (\x s -> if x < s then x else s) (tail x) s h = head x : zipWith (\x h -> if x > h then x else h) (tail x) h in unparse $ zipWith (\s h -> ("smallest ", s, " largest ", h)) s hIn

p asa index eq N where X is current x; N is current n; p = 1 fby p * X; endThe

import Data.List import Data.Maybe x `whenever` y = map fst $ filter snd $ zip x y x `asa` y = repeat $ fst $ head $ filter snd $ zip x y index = 0 : map (+1) index withCurrent = map collapseAsa a b = map head $ zipWith asa a b parse = unfoldr (listToMaybe . reads) :: String -> [Integer] unparse = concatMap (\i -> show i ++ "\n") main = interact $ \str -> let input = parse str select = True : False : select x = input `whenever` select n = input `whenever` map not select p = withCurrent (\x -> let p = 1 : map (*x) p in p) s = withCurrent (\n -> map (==n) index) in unparse $ (p x) `collapseAsa` (s n) -- s = "selector"Once again, for a really faithful translation, we should use

howfar where x = 0 fby 1 fby 2 fby 4 fby 0 fby 7 fby x; howfar = if x eq 0 then 0 else 1 + next howfar fi; end(How long until we will see another zero in the input stream x ?) But using my own zipWith with irrefutable patterns, this wil work too:

irZipWith f ~(a:as) ~(b:bs) = f a b : irZipWith f as bs main = print howfar where x = 0: 1: 2: 4: 0: 7: x howfar = irZipWith (\x h -> if x == 0 then 0 else 1 + h) x (tail howfar)A zip that can look into its own future: I would probably never have thought of it without learning Lucid, but Haskell can do it. Back to Dirk van Deun's Web 1.0 blog