Saturday, July 9, 2011

Pretending in Haskell

Is it possible in Haskell to get anything close to the dependency-tracking environment we implemented in R?.

First, there's no way it's gonna happen with regular variable assignment -- names in Haskell take on entirely unique and unchangeable values (this is right, isn't it?). So we're going to have to track names ourselves; I think using the HList module is probably appropriate.

-- Main.hs --

1 {-# LANGUAGE EmptyDataDecls #-} 2 {-# LANGUAGE DeriveDataTypeable #-} 3 {-# LANGUAGE TemplateHaskell #-} 4 5 import Data.HList 6 import Data.HList.Label4 7 import Data.HList.TypeEqGeneric1 8 import Data.HList.TypeCastGeneric1 9 import Data.HList.MakeLabels 10 11 $(makeLabels ["a", "b", "c"])

Now we have some language-accessible labels we can use. And we can build an HList Record to represent the "environment":

-- Main.hs (cont) --

13 reality = 14 a .=. 5 .*. 15 b .=. (reality#a) + 1 .*. 16 emptyRecord

This gives us the record we want,

-- Main.hs (cont) --

18 main = do 19 print reality

Record{a=5,b=6}

But changing one field won't change any of the others:

-- Main.hs (cont) --

18 main = do 19 let reality' = hUpdateAtLabel a 6 reality 20 print reality'

Record{a=6,b=6}

There are many ways we could deal with that; one way is to make reality a function and have it take itself as an argument:

-- Main.hs (cont) --

13 pReality x = 14 a .=. 5 .*. 15 b .=. (x#a) + 1 .*. 16 emptyRecord 17 18 reality = inf pReality

-- Main.hs (cont) --

20 inf f = f (inf f)

-- Main.hs (cont) --

22 main = do 23 print reality 24 let reality' = inf pReality' where 25 pReality' x = hUpdateAtLabel a 6 $ pReality x 26 print reality'

Record{a=5,b=6} Record{a=6,b=7}

That's better. We could add some syntactic sugar:

-- Main.hs (cont) --

20 pretend label value base = 21 inf (\x -> 22 hUpdateAtLabel label value (base x) 23 )

-- Main.hs (cont) --

27 main = do 28 let reality' = pretend a 6 pReality 29 print reality'

Record{a=6,b=7}

In R we kept track of which objects actually needed to be changed. He we just have a function pointing at everything; I don't know enough about how Haskell works to know whether that's inefficient; I'm not going to worry about it for now.

Notice that we cannot do this:

-- Main.hs (cont) --

13 $(label "p") 14 15 pReality x = 16 a .=. 5 .*. 17 b .=. (x#a) + 1 .*. 18 p .=. (pretend a 6 pReality) .*. 19 emptyRecord

Main.hs:15:0: Occurs check: cannot construct the infinite type: l' = HCons (LVPair (Proxy A) t) (HCons (LVPair (Proxy B) v) (HCons (LVPair (Proxy P) (Record l')) HNil)) Expected type: l' Inferred type: HCons (LVPair (Proxy A) t) (HCons (LVPair (Proxy B) v) (HCons (LVPair (Proxy P) (Record l')) HNil)) When using functional dependencies to combine HUpdateAtHNat HZero e' (HCons e l) (HCons e' l), arising from the dependency `n e l -> l'' in the instance declaration at <no location info> HUpdateAtHNat HZero (LVPair (Proxy A) t) (HCons (LVPair (Proxy A) t_a19b) (HCons (LVPair (Proxy B) v) (HCons (LVPair (Proxy P) (Record l')) HNil))) l', arising from a use of `pretend' at Main.hs:18:11-30 When generalising the type(s) for `pReality' Main.hs:29:19: No instances for (HFind (Proxy A) ls n, HUpdateAtHNat n (LVPair (Proxy A) t) t1 l') arising from a use of `pretend' at Main.hs:29:19-38 Possible fix: add an instance declaration for (HFind (Proxy A) ls n, HUpdateAtHNat n (LVPair (Proxy A) t) t1 l') In the expression: pretend a 6 pReality In the definition of `reality'': reality' = pretend a 6 pReality In the expression: do { let reality' = pretend a 6 pReality; print reality' }

But we can do this:

-- Main.hs (cont) --

13 pReality x = 14 a .=. 5 .*. 15 b .=. (x#a) + 1 .*. 16 c .=. (pretend a 6 pReality)#b .*. 17 emptyRecord

-- Main.hs (cont) --

26 main = do 27 print $ inf pReality

Record{a=5,b=6,c=7}

This is of course massively awkward, for a few reasons:

  1. We don't have a convenient way to represent assignments as actions (this is easy to fix).
  2. Haskell doesn't have anything like R's with(). This is a whole problem in itself. GHC does support dynamic binding (sort of), but it won't take an HList the way with() will take a list.

But (2) is a whole other topic for whole other day.

No comments:

Post a Comment