Compute an (infinite) tree from fixpoint operator using delay modality - haskell

Here is a functional programming puzzle involving loop-tying and infinite data structures. There is a bit of background, so hang tight.
The setting. Let us define a data type representing recursive data types:
type Var = String
data STerm = SMu Var STerm
| SVar Var
| SArrow STerm STerm
| SBottom
| STop
deriving (Show)
i.e. t ::= μα. t | α | t → t | ⊥ | ⊤. Note that ⊥ denotes the type with no inhabitants, while ⊤ denotes the type with all inhabitants. Note that (μα. α) = ⊥, as μ is a least fixpoint operator.
We can interpret a recursive data type as an infinite tree, arising from repeatedly unfolding μα. t to t[α ↦ μα. t]. (For a formal description of this process, see http://lucacardelli.name/Papers/SRT.pdf) In Haskell, we can define a type of lazy trees, which don't have μ-binders or variables:
data LTerm = LArrow LTerm LTerm
| LBottom
| LTop
deriving (Show)
and, in ordinary Haskell, a conversion function from one to the other:
convL :: [(Var, LTerm)] -> STerm -> LTerm
convL _ STop = LTop
convL _ SBottom = LBottom
convL ctx (SArrow t1 t2) = LArrow (convL ctx t1) (convL ctx t2)
convL ctx (SVar v)
| Just l <- lookup v ctx = l
| otherwise = error "unbound variable"
convL ctx (SMu v t) = fix (\r -> convL ((v, r) : ctx) t)
However, there is a problem with this function: it's not productive. If you run convL [] (SMu "x" (SVar "x")) you will infinite loop. We would rather get LBottom in this case. An interesting exercise is to directly fix this function so that it is productive; however, in this question I want to solve the problem differently.
Productivity with the delay modality. When we build cyclic data structures as above, we need to make sure we do not use the results of our computations before we have constructed them. The delay modality is a way of ensuring that we write productive (non infinite looping) programs. The basic idea is simple: if the type Int means that I have an integer today, I define a type constructor D, so that D Int means that I have a value of type Int tomorrow. D is a Functor and an Applicative (but NOT a monad.)
-- D is abstract; you are not allowed to pattern match on it
newtype D a = D a
deriving (Show)
instance Functor D where
fmap f (D a) = D (f a)
instance Applicative D where
D f <*> D a = D (f a)
pure x = D x
With D, we define a fixpoint operator: it says that to construct a value of a, you can have access to the a you are constructing, as long as you only use it tomorrow.
fixD :: (D a -> a) -> a
fixD f = f (D (fixD f))
For example, a Stream consists both of a value a I have today, and a stream Stream a which I have to produce tomorrow.
data Stream a = Cons a (D (Stream a))
Using fixD, I can define a map function on streams which is guaranteed to be productive, since the recursive call to map is only used to produced values that are needed tomorrow.
instance Functor Stream where
fmap f = fixD $ \go (Cons x xs) -> Cons (f x) (go <*> xs)
The problem. Here is a variant of LTerm with an explicit delay modality.
data Term = Arrow (D Term) (D Term)
| Bottom
| Top
deriving (Show)
Using fixD (no non-structurally recursive references allowed), how do I write a function conv :: STerm -> Term (or conv :: STerm -> D Term)?
A particularly interesting test case is SMu "x" (SArrow STop (SMu "y" (SVar "x"))); there should be no Bottoms in the resulting structure!
Update. I accidentally ruled out structural recursion on STerm, which was not the intent of the question; I've reworded to remove that restriction.

Do you intend to forbid just the unrestricted recursion (fix) in the SMu case of convL, or also the structural recursion in the SArrow case?
I don’t think this has a solution without structural recursion on STerm, because then we would have to be productive even on an infinite STerm such as:
foldr (\n -> SMu ("x" ++ show n)) undefined [0..] -- μα. μβ. μγ. μδ. …
To do this with structural recursion on STerm, it seems the trick is to store Either Term (D Term) in the context. When we pass through an Arrow and produce a D, we can convert all the Rights to Lefts.
type Ctx = [(Var, Either Term (D Term))]
dCtx :: Ctx -> D Ctx
dCtx = traverse (traverse (fmap Left . either pure id))
conv :: STerm -> Ctx -> Term
conv STop _ = Top
conv SBottom _ = Bottom
conv (SArrow t1 t2) ctx = Arrow (fmap (conv t1) (dCtx ctx)) (fmap (conv t2) (dCtx ctx))
conv (SVar v) ctx = case lookup v ctx of
Nothing -> error "unbound variable"
Just (Left t) -> t
Just (Right _) -> Bottom
conv (SMu v t) ctx = fixD (\dr -> conv t ((v, Right dr) : ctx))

My intuition is that the context should contain only delayed terms. That way, conv ctx (SMu "x" t) will be equivalent to fixD (\d -> conv ((x,r):ctx) t), as in the original convL.
If this is the case, then you need a general way to include delayed terms in your data structure, instead of just allowing them in arrows:
data Term = Arrow Term Term
| Bottom
| Top
| Next (D Term)
A first attempt at conv gives us:
conv :: [(Var, D Term)] -> STerm -> Term
conv _ STop = Top
conv _ SBottom = SBottom
conv ctx (SArrow t1 t2) = Arrow (conv ctx t1) (conv ctx t2)
conv ctx (SVar v)
| Just l <- lookup v ctx = Next l
| otherwise = error "unbound variable"
conv ctx (SMu v t) = fixD (\r -> conv ((x,r):ctx) t)
However, this uses unguarded recursive calls to conv. If you want to avoid that, you can wrap all the fixD recursive calls in a Next.
conv :: [(Var, D Term)] -> STerm -> Term
conv = fixD step where
step _ _ STop = Top
step _ _ SBottom = Bottom
step d ctx (SArrow t1 t2) = Arrow (Next $ d <*> pure ctx <*> pure t1)
(Next $ d <*> pure ctx <*> pure t2)
step d ctx (SVar v)
| Just l <- lookup v ctx = Next l
| otherwise = error "unbound variable"
step d ctx (SMu v t) = fixD (\r ->
Next $ d <*> pure ((x,r):ctx) <*> pure t)
I'm not sure if this is exactly what you are looking for, because conv [] SMu "x" (SArrow SBottom (SMu "y" (SVar "x"))) does still have bottoms in the resulting structure. What is the type you want to get out?

Related

Selectively recurse into left or right subtree of a binary tree using a catamorphism (or any recursion scheme)

I'm trying to implement a binary search tree (or set) using fixed points of functors. I've defined my fixed point as follows:
newtype Fix f = In (f (Fix f))
out :: Fix f -> f (Fix f)
out (In f) = f
-- Catamorphism
type Algebra f a = f a -> a
cata :: (Functor f) => Algebra f a -> Fix f -> a
cata f = f . fmap (cata f) . out
To make the binary tree, I'm using a red-black tree like so:
data NodeColor = Red | Black deriving (Eq, Show)
data RedBlackTreeF a r = EmptyRedBlackTreeF | RedBlackTreeNodeF NodeColor r a r deriving (Eq, Show)
instance Functor (RedBlackTreeF a) where
fmap _ EmptyRedBlackTreeF = EmptyRedBlackTreeF
fmap f (RedBlackTreeNodeF color r1 a r2) =
RedBlackTreeNodeF color (f r1) a (f r2)
type RedBlackTreeF' a = Fix (RedBlackTreeF a)
The traditional benefit of a binary tree is being able to cut down search time by choosing whether to search further in the left or right subtree like so (in psuedocode):
fun member (x, E) = false
| member (x, T (_, a, y, b)) =
if x < y then member (x, a)
else if x > y then member (x, b)
else true
The function member will go left if the element that is being searched for is less than the current element and right if the opposite is true. It therefore improves search time to O(logn).
However, in a recursion scheme, the algebra is recursively applied to the entire data structure. I've written an member algebra here:
memberPAlg :: Ord a => a -> RedBlackTreeF a Bool -> Bool
memberPAlg _ EmptyRedBlackTreeF = False
memberPAlg elem (RedBlackTreeNodeF _ left cur right) =
(elem == cur) || (left || right)
But it seems to be O(nlogn) rather than O(logn). Is there any way to selectively recurse using a recursion scheme to save time complexity? Am I thinking about this the wrong way?
Because of laziness, left and right are evaluated only if you ask for them. So, just compare the current node with the value being searched for to decide which subtree to go into:
memberPAlg :: Ord a => a -> RedBlackTreeF a Bool -> Bool
memberPAlg _ EmptyRedBlackTreeF = False
memberPAlg elem (RedBlackTreeNodeF _ left cur right) =
case compare elem cur of
EQ -> True
LT -> left
GT -> right

Pattern matching in `Alternative`

I have a function that pattern matches on its arguments to produce a computation in StateT () Maybe (). This computation can fail when run, in which case I want the current pattern match branch to fail, so to speak.
I highly doubt it's possible to have something like
compute :: Int -> StateT () Maybe Int
compute = return
f :: Maybe Int -> Maybe Int -> StateT () Maybe ()
f (Just n1) (Just n2) = do
m <- compute (n1 + n2)
guard (m == 42)
f (Just n) _ = do
m <- compute n
guard (m == 42)
f _ (Just n) = do
m <- compute n
guard (m == 42)
behave in the way I want it to: When the first computation fails due to the guard or somewhere in compute, I want f to try the next pattern.
Obviously the above can't work, because StateT (as any other monad might) involves an additional parameter when expanded, so I probably can't formulate this as simple pattern guards.
The following does what I want, but it's ugly:
f' :: Maybe Int -> Maybe Int -> StateT () Maybe ()
f' a b = asum (map (\f -> f a b) [f1, f2, f3])
where
f1 a b = do
Just n1 <- pure a
Just n2 <- pure b
m <- compute (n1 + n2)
guard (m == 42)
f2 a _ = do
Just n <- pure a
m <- compute n
guard (m == 42)
f3 _ b = do
Just n <- pure b
m <- compute n
guard (m == 42)
A call like execStateT (f (Just 42) (Just 1)) () would fail for f but return Just () for f', because it matches f2.
How do I get the behavior of f' while having elegant pattern matching with as little auxiliary definitions as possible like in f? Are there other, more elegant ways to formulate this?
Complete runnable example:
#! /usr/bin/env stack
-- stack --resolver=lts-11.1 script
import Control.Monad.Trans.State
import Control.Applicative
import Control.Monad
import Data.Foldable
compute :: Int -> StateT () Maybe Int
compute = return
f :: Maybe Int -> Maybe Int -> StateT () Maybe ()
f (Just n1) (Just n2) = do
m <- compute (n1 + n2)
guard (m == 42)
f (Just n) _ = do
m <- compute n
guard (m == 42)
f _ (Just n) = do
m <- compute n
guard (m == 42)
f' :: Maybe Int -> Maybe Int -> StateT () Maybe ()
f' a b = asum (map (\f -> f a b) [f1, f2, f3])
where
f1 a b = do
Just n1 <- pure a
Just n2 <- pure b
m <- compute (n1 + n2)
guard (m == 42)
f2 a _ = do
Just n <- pure a
m <- compute n
guard (m == 42)
f3 _ b = do
Just n <- pure b
m <- compute n
guard (m == 42)
main = do
print $ execStateT (f (Just 42) (Just 1)) () -- Nothing
print $ execStateT (f' (Just 42) (Just 1)) () -- Just (), because `f2` succeeded
Edit: I elicited quite some clever answers with this question so far, thanks! Unfortunately, they mostly suffer from overfitting to the particular code example I've given. In reality, I need something like this for unifying two expressions (let-bindings, to be precise), where I want to try unifying the RHS of two simultaneous lets if possible and fall through to the cases where I handle let bindings one side at a time by floating them. So, actually there's no clever structure on Maybe arguments to exploit and I'm not computeing on Int actually.
The answers so far might benefit others beyond the enlightenment they brought me though, so thanks!
Edit 2: Here's some compiling example code with probably bogus semantics:
module Unify (unify) where
import Control.Applicative
import Control.Monad.Trans.State.Strict
data Expr
= Var String -- meta, free an bound vars
| Let String Expr Expr
-- ... more cases
-- no Eq instance, fwiw
-- | If the two terms unify, return the most general unifier, e.g.
-- a substitution (`Map`) of meta variables for terms as association
-- list.
unify :: [String] -> Expr -> Expr -> Maybe [(String, Expr)]
unify metaVars l r = execStateT (go [] [] l r) [] -- threads the current substitution as state
where
go locals floats (Var x) (Var y)
| x == y = return ()
go locals floats (Var x) (Var y)
| lookup x locals == Just y = return ()
go locals floats (Var x) e
| x `elem` metaVars = tryAddSubstitution locals floats x e
go locals floats e (Var y)
| y `elem` metaVars = tryAddSubstitution locals floats y e
-- case in point:
go locals floats (Let x lrhs lbody) (Let y rrhs rbody) = do
go locals floats lrhs rrhs -- try this one, fail current pattern branch if rhss don't unify
-- if we get past the last statement, commit to this branch, no matter
-- the next statement fails or not
go ((x,y):locals) floats lbody rbody
-- try to float the let binding. terms mentioning a floated var might still
-- unify with a meta var
go locals floats (Let x rhs body) e = do
go locals (Left (x,rhs):floats) body e
go locals floats e (Let y rhs body) = do
go locals (Right (y,rhs):floats) body e
go _ _ _ _ = empty
tryAddSubstitution = undefined -- magic
When I need something like this, I just use asum with the blocks inlined. Here I also condensed the multiple patterns Just n1 <- pure a; Just n2 <- pure b into one, (Just n1, Just n2) <- pure (a, b).
f :: Maybe Int -> Maybe Int -> StateT () Maybe ()
f a b = asum
[ do
(Just n1, Just n2) <- pure (a, b)
m <- compute (n1 + n2)
guard (m == 42)
, do
Just n <- pure a
m <- compute n
guard (m == 42)
, do
Just n <- pure b
m <- compute n
guard (m == 42)
]
You can also use chains of <|>, if you prefer:
f :: Maybe Int -> Maybe Int -> StateT () Maybe ()
f a b
= do
(Just n1, Just n2) <- pure (a, b)
m <- compute (n1 + n2)
guard (m == 42)
<|> do
Just n <- pure a
m <- compute n
guard (m == 42)
<|> do
Just n <- pure b
m <- compute n
guard (m == 42)
This is about as minimal as you can get for this kind of “fallthrough”.
If you were using Maybe alone, you would be able to do this with pattern guards:
import Control.Monad
import Control.Applicative
ensure :: Alternative f => (a -> Bool) -> a -> f a
ensure p a = a <$ guard (p a)
compute :: Int -> Maybe Int
compute = return
f :: Maybe Int -> Maybe Int -> Maybe Int
f (Just m) (Just n)
| Just x <- ensure (== 42) =<< compute (m + n)
= return x
f (Just m) _
| Just x <- ensure (== 42) =<< compute m
= return x
f _ (Just n)
| Just x <- ensure (== 42) =<< compute n
= return x
f _ _ = empty
(ensure is a general purpose combinator. Cf. Lift to Maybe using a predicate)
As you have StateT on the top, though, you would have to supply a state in order to pattern match on Maybe, which would foul up everything. That being so, you are probably better off with something in the vein of your "ugly" solution. Here is a whimsical attempt at improving its looks:
import Control.Monad
import Control.Applicative
import Control.Monad.State
import Control.Monad.Trans
import Data.Foldable
ensure :: Alternative f => (a -> Bool) -> a -> f a
ensure p a = a <$ guard (p a)
compute :: Int -> StateT () Maybe Int
compute = return
f :: Maybe Int -> Maybe Int -> StateT () Maybe Int
f a b = asum (map (\c -> f' (c a b)) [liftA2 (+), const, flip const])
where
f' = ensure (== 42) <=< compute <=< lift
While this is an answer specific to the snippet I've given, the refactorings only apply limited to the code I was facing.
Perhaps it's not that far-fetched of an idea to extract the skeleton of the asum expression above to a more general combinator:
-- A better name would be welcome.
selector :: Alternative f => (a -> a -> a) -> (a -> f b) -> a -> a -> f b
selector g k x y = asum (fmap (\sel -> k (sel x y)) [g, const, flip const])
f :: Maybe Int -> Maybe Int -> StateT () Maybe Int
f = selector (liftA2 (+)) (ensure (== 42) <=< compute <=< lift)
Though it is perhaps a bit awkward of a combinator, selector does show the approach is more general than it might appear at first: the only significant restriction is that k has to produce results in some Alternative context.
P.S.: While writing selector with (<|>) instead of asum is arguably more tasteful...
selector g k x y = k (g x y) <|> k x <|> k y
... the asum version straightforwardly generalises to an arbitrary number of pseudo-patterns:
selector :: Alternative f => [a -> a -> a] -> (a -> f b) -> a -> a -> f b
selector gs k x y = asum (fmap (\g -> k (g x y)) gs)
It looks like you could get rid of the whole pattern match by relying on the fact that Int forms a Monoid with addition and 0 as the identity element, and that Maybe a forms a Monoid if a does. Then your function becomes:
f :: Maybe Int -> Maybe Int -> StateT () Maybe Int
f a b = pure $ a <> b >>= compute >>= pure . mfilter (== 42)
You could generalise by passing the predicate as an argument:
f :: Monoid a => (a -> Bool) -> Maybe a -> Maybe a -> StateT () Maybe a
f p a b = pure $ a <> b >>= compute >>= pure . mfilter p
The only thing is that compute is now taking a Maybe Int as input, but that is just a matter of calling traverse inside that function with whatever computation you need to do.
Edit: Taking into account your last edit, I find that if you spread your pattern matches into separate computations that may fail, then you can just write
f a b = f1 a b <|> f2 a b <|> f3 a b
where f1 (Just a) (Just b) = compute (a + b) >>= check
f1 _ _ = empty
f2 (Just a) _ = compute a >>= check
f2 _ _ = empty
f3 _ (Just b) = compute b >>= check
f3 _ _ = empty
check x = guard (x == 42)

Using Comonad Fix Combinators

So I've been experimenting with fixed points lately and have finally struggled
through regular fixed points enough to discover some uses; now I'm moving onto
comonadic fixed points and I'm afraid I've gotten stuck;
Here's a few examples of what I've tried and what has/hasn't worked:
{-# language DeriveFunctor #-}
{-# language FlexibleInstances #-}
module WFix where
import Control.Comonad
import Control.Comonad.Cofree
import Control.Monad.Fix
So I started with loeb's theorem as a list; each element of the list is a function
which takes the end result to compute its answer; this lets me do 'spreadsheet'
calculations where values can depend on other values.
spreadSheetFix :: [Int]
spreadSheetFix = fix $ \result -> [length result, (result !! 0) * 10, (result !! 1) + 1, sum (take 3 result)]
Okay, so I have basic fix working, time to move on to the comonad types!
Here's a few simple comonads to use for examples:
data Stream a = S a (Stream a)
deriving (Eq, Show, Functor)
next :: Stream a -> Stream a
next (S _ s) = s
instance Comonad Stream where
extract (S a _) = a
duplicate s#(S _ r) = S s (duplicate r)
instance ComonadApply Stream where
(S f fs) <#> (S a as) = S (f a) (fs <#> as)
data Tape a = Tape [a] a [a]
deriving (Show, Eq, Functor)
moveLeft, moveRight :: Tape a -> Tape a
moveLeft w#(Tape [] _ _) = w
moveLeft (Tape (l:ls) a rs) = Tape ls l (a:rs)
moveRight w#(Tape _ _ []) = w
moveRight (Tape ls a (r:rs)) = Tape (a:ls) r rs
instance Comonad Tape where
extract (Tape _ a _) = a
duplicate w#(Tape l _ r) = Tape lefts w rights
where
lefts = zipWith const (tail $ iterate moveLeft w) l
rights = zipWith const (tail $ iterate moveRight w) r
instance ComonadApply Tape where
Tape l f r <#> Tape l' a r' = Tape (zipWith ($) l l') (f a) (zipWith ($) r r')
Okay so the following combinators come from Control.Comonad;
wfix :: Comonad w => w (w a -> a) -> a
wfix w = extract w (extend wfix w)
cfix :: Comonad w => (w a -> a) -> w a
cfix f = fix (extend f)
kfix :: ComonadApply w => w (w a -> a) -> w a
kfix w = fix $ \u -> w <#> duplicate u
I started with trying out wfix:
streamWFix :: Int
streamWFix = wfix st
where
incNext = succ . extract . next
st = (S incNext (S incNext (S (const 0) st)))
> streamWFix
-- 2
This one seems to work by calling the first w a -> a on w until reaching
a resolution const 0 in this case; that makes sense. We can also do this
with a Tape:
selfReferentialWFix :: Int
selfReferentialWFix = wfix $ Tape [const 10] ((+5) . extract . moveLeft) []
-- selfReferentialWFix == 15
K, I think I get that one, but the next ones I'm kind of stuck,
I don't seem to have an intuition for what cfix is supposed to do.
Even the simplest possible thing I could think of spins forever
when I evaluate it; even trying to extract the first element of the stream
using getOne fails.
getOne :: Stream a -> a
getOne (S a _) = a
simpleCFix :: Stream Int
simpleCFix = cfix go
where
go _ = 0
Similarly with kfix; even simple tries don't seem to terminate.
My understanding of kfix was that the function in each 'slot' gets
passed a copy of the evaluated comonad focused on that spot; is that the case?
I tried using 'getOne' on this:
streamKFix :: Stream Int
streamKFix = kfix st
where
go _ = 0
st = S go st
Here's a finite attempt using Tape which also fails to run:
tapeKFix :: Tape Int
tapeKFix = kfix $ Tape [] (const 0) []
So; down to my question, could someone please offer some runnable (non-trivial)
examples of using cfix and kfix, and explain how they work? I plan to use kfix to eventually do a "Conway's
game of life" style experiment, am I correct in thinking that kfix would be useful
in working with neighbourhoods around a given cell?
Feel free to ask
any clarifying questions and help me expand my knowledge and intuition of fix!
Thanks!
The ComonadApply and Comonad instances for Tape are insufficiently lazy to be used with kfix.
duplicate for Tape requires that you prove the tape exists before it can conclude that the result is a Tape
instance Comonad Tape where
extract (Tape _ a _) = a
duplicate w#(Tape l _ r) = Tape lefts w rights
-- ^ ^
-- matches a Tape |
-- before determining that the result is a Tape
<#> checks that both arguments are tapes before it can conclude that the result is a Tape
instance ComonadApply Tape where
Tape l f r <#> Tape l' a r' = Tape (zipWith ($) l l') (f a) (zipWith ($) r r')
-- ^ ^ ^
-- matches two Tapes |
-- before detrmining that the result is a Tape
Combined there's no way for kfix (Tape _ _ _) to ever produce Tape
kfix w = fix $ \u -> w <#> duplicate u
kfix (Tape _ _ _) = fix $ \u -> (Tape _ _ _) <#> duplicate u
kfix (Tape _ _ _) = fix $ \u -> (Tape _ _ _) <#> case u of (Tape _ _ _) -> ...
-- ^ |
-- ----------- <<loop>> -------------
You can fix this by making duplicate, <#>, or both more productive by using irrefutable patterns. A pattern ~(Tape l a r) matches even if the Tape constructor hasn't been produced yet. Here's how you'd use it to make duplicate productive
instance Comonad Tape where
extract (Tape _ a _) = a
duplicate w#(~(Tape l _ r)) = Tape lefts w rights
where
lefts = zipWith const (tail $ iterate moveLeft w) l
rights = zipWith const (tail $ iterate moveRight w) r
Irrefutable pattern matches are equivalent to using functions to extract values. For duplicate it's equivalent to writing
left (Tape l _ _) = l
right (Tape _ _ r) = r
instance Comonad Tape where
extract (Tape _ a _) = a
duplicate w = Tape lefts w rights
where
l = left w
r = right w
...

Iterating over two sets while updating two other sets in Haskell

I am trying to translate the following imperative code to a functional solution in Haskell. I want to compare the members of set s with members of set s' and update sets t and t' based on the comparison. Here is the imperative pseudocode:
-- s, s', t, t' are of type Data.Set a
-- foo x y returns a Just a or Nothing
foo :: a -> a -> Just a
-- Initialize t and t' to s and s'
t = s
t = s'
foreach x in s
foreach y in s'
if (foo x y) == Just z
insert z into t
delete x from t
delete y from t'
return (t, t')
The type of the Haskell function I am wishing for may be something like,
mergeSets :: S.Set -> S.Set -> (S.Set, S.Set)
mergeSets s s' = ...
where S is type Data.Set and the result of the function will be a pair with the new sets t and t' (or some other way to return both sets t and t').
Here's one possibility:
bar s s' =
foldl (\ (t,t') (z,(x,y)) ->
( delete x (insert z t) , delete y t' ))
(s,s')
[(z,(x,y)) | x <- toList s, y <- toList s', Just z <- [foo x y]]
The main question here is whether you intended for your insertions and deletions to interfere with the foreach mechanism. The above assumes that you did not.
If your sets are any large, you may need to add strictness, to avoid thunks blow-up:
bar s s' = foldl (\ (t,t') (z,(x,y)) ->
let a=insert z t; b=delete x a; c=delete y t'
in a `seq` b `seq` c `seq` (b,c) )
....
If you're working with a collection data type like Set you're typically not going to write loops over the elements. That would be more appropriate for lists. So if your algorithm requires enumerating all the elementss in some nested way, convert the Set to a list.
So, I would try to avoid using your nested loop algo entirely on sets, and instead look for some declarative specification in terms of set operations: intersection, union, difference etc.
If that's not possible, a naive translation to lists is certainly possible:
import qualified Data.Set as S
mergeSets :: Ord a => S.Set a -> S.Set a -> (S.Set a, S.Set a)
mergeSets s t = go1 (S.toList s) s t
where
go1 [] t t' = (t,t')
go1 (x:xs) t t' = go1 xs u u'
where
(u, u') = go2 x (S.toList t) t t'
go2 x [] t t' = (t,t')
go2 x (y:ys) t t'
| Just z <- foo x y = go2 x ys (S.delete x (S.insert z t)) (S.delete y t')
| otherwise = go2 x ys t t'
-- for example
foo x y = if x == y then Just x else Nothing
The simple bit is modelling a nested loop. We could have used e.g. a list comprehension. However, your algo uses mutation of the output sets, so we need to pass that as an accumulating parameter.
To really get this nicely into Haskell though, we need to give up the element-by-element imperative style, and work in terms of the natural Set api. That will be your road to a nice solution.

Implementing unify algorithm in haskell

I am trying to implement a unify function with an algorithm that is specified as
unify α α = idSubst
unify α β = update (α, β) idSubst
unify α (τ1 ⊗ τ2) =
if α ∈ vars(τ1 ⊗ τ2) then
error ”Occurs check failure”
else
update (α, τ1 ⊗ τ2) idSubst
unify (τ1 ⊗ τ2) α = unify α (τ1 ⊗ τ2)
unify (τ1 ⊗1 τ2) (τ3 ⊗2 τ4) = if ⊗1 == ⊗2 then
(subst s2) . s1
else
error ”not unifiable.”
where s1 = unify τ1 τ3
s2 = unify (subst s1 τ2) (subst s1 τ4)
with ⊗ being one of the type constructors {→, ×}.
However I do not understand how to implement this in haskell. How would I go about this?
import Data.List
import Data.Char
data Term = Var String | Abs (String,Term) | Ap Term Term | Pair Term Term | Fst Term | Snd Term
deriving (Eq,Show)
data Op = Arrow | Product deriving (Eq)
data Type = TVar String | BinType Op Type Type
deriving (Eq)
instance Show Type where
show (TVar x) = x
show (BinType Arrow t1 t2) = "(" ++ show t1 ++ " -> " ++ show t2 ++ ")"
show (BinType Product t1 t2) = "(" ++ show t1 ++ " X " ++ show t2 ++ ")"
type Substitution = String -> Type
idSubst :: Substitution
idSubst x = TVar x
update :: (String, Type) -> Substitution -> Substitution
update (x,y) f = (\z -> if z == x then y else f z)
-- vars collects all the variables occuring in a type expression
vars :: Type -> [String]
vars ty = nub (vars' ty)
where vars' (TVar x) = [x]
vars' (BinType op t1 t2) = vars' t1 ++ vars' t2
subst :: Substitution -> Type -> Type
subst s (TVar x) = s x
subst s (BinType op t1 t2) = BinType op (subst s t1) (subst s t2)
unify :: Type -> Type -> Substitution
unify (TVar x) (TVar y) = update (x, TVar y) idSubst
unify :: Type -> Type -> Substitution
unify (TVar x) (TVar y) = update (x, TVar y) idSubst
This is a great start!
Now you just need to handle the other cases:
Here's how you'd represent the first one:
unify (TVar x) (TVar y) | x == y = idSubst
You can do the rest similarly using pattern matching to decompose your Type into the appropriate constructors and guards to handle specific cases.
Haskell has an error :: String -> a function that works the same as in your pseudo-code above, and the if/then/else syntax is the same, so you're almost there!

Resources