Safe Haskell | Safe-Inferred |
---|---|

Language | Haskell2010 |

## Synopsis

- letrecE :: forall q tag r a. (Ord tag, Quote q, MonadFix q) => (forall. tag -> String) -> (forall m. Monad m => (tag -> m (Code q a)) -> tag -> m (Code q a)) -> (forall m. Monad m => (tag -> m (Code q a)) -> m (Code q r)) -> Code q r
- letrecH :: forall q tag r. (GCompare tag, Quote q, MonadFix q) => (forall x. tag x -> String) -> (forall m y. Monad m => (forall x. tag x -> m (Code q x)) -> tag y -> m (Code q y)) -> (forall m. Monad m => (forall x. tag x -> m (Code q x)) -> m (Code q r)) -> Code q r

# Documentation

:: forall q tag r a. (Ord tag, Quote q, MonadFix q) | |

=> (forall. tag -> String) | tag naming function |

-> (forall m. Monad m => (tag -> m (Code q a)) -> tag -> m (Code q a)) | bindings generator (with recursive function) |

-> (forall m. Monad m => (tag -> m (Code q a)) -> m (Code q r)) | final expression generator |

-> Code q r | generated let expression |

Generate potentially recursive let expression.

Example of generating a list ofg alternative `True`

and `False`

values.

`>>>`

`let trueFalse = letrecE (\tag -> "go" ++ show tag) (\rec tag -> rec (not tag) >>= \next -> return [|| $$(TH.liftTyped tag) : $$next ||]) (\rec -> rec True)`

The generated let-bindings looks like:

`>>>`

let {goFalse_0 = GHC.Types.False GHC.Types.: goTrue_1; goTrue_1 = GHC.Types.True GHC.Types.: goFalse_0} in goTrue_1`TH.ppr <$> TH.unTypeCode trueFalse`

And when spliced it produces a list of alternative `True`

and `False`

values:

`>>>`

[True,False,True,False,True,False,True,False,True,False]`take 10 $$trueFalse`

:: forall q tag r. (GCompare tag, Quote q, MonadFix q) | |

=> (forall x. tag x -> String) | tag naming function |

-> (forall m y. Monad m => (forall x. tag x -> m (Code q x)) -> tag y -> m (Code q y)) | bindings generator (with recursive function) |

-> (forall m. Monad m => (forall x. tag x -> m (Code q x)) -> m (Code q r)) | final expression generator |

-> Code q r | generated let expression |

Generate potentially recursive let expression with heterogenously typed bindings.

A simple example is consider a case where you have a `NP`

(from `sop-core`

) of `Code`

values

`>>>`

data NP f xs where Nil :: NP f '[] (:*) :: f x -> NP f xs -> NP f (x : xs) infixr 5 :* :}`:{`

`>>>`

let values :: TH.Quote q => NP (Code q) '[ Bool, Char ] values = [|| True ||] :* [|| 'x' ||] :* Nil :}`:{`

and function from that to a single `Code`

`>>>`

let gen :: TH.Quote q => NP (Code q) '[ Bool, Char ] -> Code q String gen (x :* y :* Nil) = [|| $$y : $$y : show $$x ||] :}`:{`

We can apply `gen`

to `values`

to get a code expression:

`>>>`

'x' GHC.Types.: ('x' GHC.Types.: GHC.Show.show GHC.Types.True)`TH.ppr <$> TH.unTypeCode (gen values)`

But if `values`

where big, we would potentially duplicate the computations.
Better to first let-bind them.

We'll need a type to act as a tag:

`>>>`

data Idx xs x where IZ :: Idx (x ': xs) x IS :: Idx xs x -> Idx (y ': xs) x instance GEq (Idx xs) where geq = defaultGeq instance GCompare (Idx xs) where gcompare IZ IZ = GEQ gcompare (IS x) (IS y) = gcompare x y gcompare IZ (IS _) = GLT gcompare (IS _) IZ = GGT :}`:{`

Using `Idx`

we can index `NP`

values:

`>>>`

let index :: NP f xs -> Idx xs x -> f x index (x :* _) IZ = x index (_ :* xs) (IS i) = index xs i :}`:{`

And with some extra utilities

`>>>`

`mapNP :: (forall x. f x -> g x) -> NP f xs -> NP g xs; mapNP _ Nil = Nil; mapNP f (x :* xs) = f x :* mapNP f xs`

`>>>`

`traverseNP :: Applicative m => (forall x. f x -> m (g x)) -> NP f xs -> m (NP g xs); traverseNP _ Nil = pure Nil; traverseNP f (x :* xs) = (:*) <$> f x <*> traverseNP f xs`

`>>>`

`indices :: NP f xs -> NP (Idx xs) xs; indices Nil = Nil; indices (_ :* xs) = IZ :* mapNP IS (indices xs) -- first argument acts as list singleton`

we can make a combinator for generating dynamic let-expression:

`>>>`

let letNP :: (Quote q, MonadFix q) => NP (Code q) xs -> (NP (Code q) xs -> Code q r) -> Code q r letNP vals g = letrecH (\_ -> "x") (\_rec idx -> return (index vals idx)) (\rec -> do { vals' <- traverseNP rec (indices vals); return (g vals') }) :}`:{`

and use it to bind `values`

before using them in `gen`

:

`>>>`

let {x_0 = GHC.Types.True; x_1 = 'x'} in x_1 GHC.Types.: (x_1 GHC.Types.: GHC.Show.show x_0)`TH.ppr <$> TH.unTypeCode (letNP values gen)`

The result of evaluating either expression is the same:

`>>>`

"xxTrue"`$$(gen values)`

`>>>`

"xxTrue"`$$(letNP values gen)`

This example illustrates that `letrecH`

is more general than something
like `letNP`

and doesn't require extra data-structures
(Instead of having `GCompare`

constraint the function can ask for `tag x -> tag y -> Maybe (x :~: y)`

function)