# [Haskell-cafe] manage effects in a DSL

Corentin Dupont corentin.dupont at gmail.com
Wed Feb 12 16:44:17 UTC 2014

```Hi guys,
so I tried to implement fully the proposition (see below).
It works well. However I find it a bit redundant. Can we reduce the
repetitions?
Perhaps I didn't understand how to write the evaluation...

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
module Main where

class Monad m => Nomex m where

class Nomex m => NomexEffect m where
writeAccount :: Int -> m ()
setVictory   :: (forall n. Nomex n => n Bool) -> m ()

data Exp a where
WriteAccount :: Int -> Exp ()
SetVictory   :: (forall m. Nomex m => m Bool) -> Exp ()
Bind         :: Exp a -> (a -> Exp b) -> Exp b
Return       :: a -> Exp a

return = Return
(>>=) = Bind

instance Nomex Exp where

instance NomexEffect Exp where
writeAccount = WriteAccount
setVictory   = SetVictory

data Game = Game { victory :: (forall m. Nomex m => m Bool)
, account :: Int
}

instance Nomex (State Game) where

instance NomexEffect (State Game) where
writeAccount n = modify \$ \game -> game { account = n }
setVictory   v = modify \$ \game -> game { victory = v }

evaluate :: Exp a -> State Game a
evaluate (WriteAccount i) = writeAccount i
evaluate (SetVictory v) = setVictory v
evaluate (Return a) = return a
evaluate (Bind a f) = (evaluate a) >>= evaluate . f

evalNoEff :: Exp a -> Reader Game a
evalNoEff (Return a) = return a
evalNoEff (Bind a f) = (evalNoEff a) >>= evalNoEff . f

isVictory :: Game -> Bool
isVictory g = runReader (evalNoEff (victory g)) g

incrAccount :: NomexEffect m => m ()
incrAccount = readAccount >>= writeAccount . (+101)

winOnBigMoney :: NomexEffect m => m ()
winOnBigMoney = setVictory \$ do
--writeAccount 100
return (i > 100)

play = do
winOnBigMoney
incrAccount

initGame = Game (return False) 0

main = do
let g = execState (evaluate jeu) initGame
putStrLn \$ show \$ isVictory g

On Mon, Feb 10, 2014 at 11:33 AM, Dominique Devriese <
dominique.devriese at cs.kuleuven.be> wrote:

> Corentin,
>
> 2014-02-10 10:48 GMT+01:00 Corentin Dupont <corentin.dupont at gmail.com>:
> > That is really interesting. In fact, I didn't have the time to experiment
> > with it, but I definitely want to (have to find some spare time!).
> > I must say I am less used to type classes.
> > At first, my concern with the technique was that two things that belong
> > together, "ReadAccount" and "WriteAccount", are separated.
>
> Yes, this separation of ReadAccount and WriteAccount into Nomex vs
> NomexEffect is how the two parts (read-only vs read-write) of the DSL
> are distinguished in this approach..
>
> > I was also confused that the evaluator is wrapped in a newtype, and that
> it
> > is an instance of Nomex.
>
> That is non-essential.  You can also use
>
>   instance Nomex (State Game) where
>
> but it's just cleaner with a newtype...
>
> > Beside, I suppose it is possible to factorize EvalNoEffect with Eval?
> Maybe
> > using liftEval anyway...
>
> If I understand correctly, you're asking about how to remove the
> duplication between EvalNoEffect and Eval?
>
> This is a very good question.  My answer is basically that Haskell is
> missing some type-class-related features to allow for the perfect
> solution, specifically a form of local instances.
>
> The long story is that instead of the above instances of Nomex and
> NomexEffect for Eval and EvalNoEffect separately, we would like to be
> able to write the following instances:
>
>
>     writeAccount n = modify \$ \game -> game { account = n }
>     setVictory   v = modify \$ \game -> game { victory = v }
>
> and then we can declare
>   newtype Eval a = Eval { eval :: State Game a }
>
>   newtype EvalNoEffect a = EvalNoEffect { evalNoEffect :: Reader Game a }
>
> and reuse the single implementation of Nomex for both Eval and
> EvalNoEffect.  However, there are various problems with this solution:
>
> * the instances are not permitted without UndecidableInstances (which
> I recommend against),
> despite the fact that functionality-wise it is.
>
> What is needed to solve these problems is a feature that is in my
> opinion strongly missing in Haskell: a form of local instances.  This
> means that we would be able to explicitly specify what implementation
> of a certain type class should be used to satisfy a certain type class
> constraint, e.g.
>
>   sort :: Ord a => [a] -> [a]
>   sortBy :: forall a. (a -> a -> Bool) -> [a] -> [a]
>   sortBy f = let instance ordDict :: Ord.Dict a
>                                         ordDict = constructOrdDict f
>                    in sort :: Ord a => [a] -> [a]
>
> type classes, but they are problematic to combine with type inference.
>  However, it seems likely that this is not an issue if we require
> sufficiently informative type annotations.
>
> For the problem above, this would allow to construct, use and lift
> State monad without necessarily having it derived automatically if
> this is not desired.  Also, this would allow to write the undecidable
> instances as normal functions that need to be explicitly invoked
> instead of inferred by type inference, avoiding the
> UndecidableInstances problem.
>
> Regards
> Dominique
>
-------------- next part --------------
An HTML attachment was scrubbed...