Type variable error - haskell

I am currently working on a game engine in Haskell and now I am trying to implement a messaging system between subsystems of this engine (I am using a Publisher-Subscriber pattern). You can check out the code as a whole at here on my github profile. I implemented a "Participant" typeclass for all datatypes participating in my Messaging system as follows:
class (Show m, Message m) => Participant prt m where
-- | Function to get the lsit of subscribers from the participant
partSubscribers
:: prt
-- ^ the participant
-> forall us. Affection us [(m -> Affection us ())]
-- ^ List of Subscriber functions
-- | Subscribe to the 'Participant''s events
partSubscribe
:: prt
-- ^ The 'Participant' to subscribe to
-> (forall us. m -> Affection us ())
-- ^ What to do in case of a 'Message'
-- (Subscriber function)
-> Affection us UUID
-- ^ 'UUID' of the registered subscriber Function
-- | Unsubscribe a Subscriber function from Participant
partUnSubscribe
:: prt -- ^ The 'Participant' to unsubscribe from
-> UUID -- ^ The subscriber function's 'UUID'
-> Affection us ()
-- | Get the 'Participant' to emit a 'Message' on all of its subscribers
partEmit
:: prt -- ^ The 'Participant'
-> m -- ^ The 'Message' to emit
-> Affection us ()
partEmit p m = do
liftIO $ logIO Debug $ "Emitting message: " ++ show m
l <- partSubscribers p
mapM_ ($ m) l
A concrete implementation of this Typeclass looks like this. I'm making the use of the Package stm:
data AffectionWindow us = AffectionWindow
{ windowSubscribers
:: forall us. TVar [(UUID, WindowMessage -> Affection us ())]
}
instance Participant (AffectionWindow us) WindowMessage where
partSubscribe p funct = do
uuid <- genUUID
liftIO $ atomically $ modifyTVar' (windowSubscribers p) ((uuid, funct) :)
return uuid
partUnSubscribe p uuid =
liftIO $ atomically $ modifyTVar' (windowSubscribers p)
(filter (\(u, _) -> u /= uuid))
partSubscribers p = do
subTups <- liftIO $ readTVarIO $ windowSubscribers p
return $ map snd subTups
This code compiles fine as a library, but when I try to use this in a minimal example, it fails to compile. The code, where it fails looks like this:
load :: IO StateData
load = do
empty1 <- newTVarIO []
-- ([] :: [(UUID, WindowMessage -> Affection StateData ())])
empty2 <- newTVarIO []
-- ([] :: [(UUID, MouseMessage -> Affection StateData ())])
empty3 <- newTVarIO []
-- ([] :: [(UUID, KeyboardMessage -> Affection StateData ())])
return $ StateData $ Subsystems
(AffectionWindow empty1)
(AffectionMouse empty2)
(AffectionKeyboard empty3)
and the error message is:
examples/example00.hs:43:22: error:
• Couldn't match type ‘a0’
with ‘(UUID, WindowMessage -> Affection us1 ())’
because type variable ‘us1’ would escape its scope
This (rigid, skolem) type variable is bound by
a type expected by the context:
forall us1. TVar [(UUID, WindowMessage -> Affection us1 ())]
at examples/example00.hs:43:6-27
Expected type: TVar [(UUID, WindowMessage -> Affection us1 ())]
Actual type: TVar [a0]
• In the first argument of ‘AffectionWindow’, namely ‘empty1’
In the first argument of ‘Subsystems’, namely
‘(AffectionWindow empty1)’
In the second argument of ‘($)’, namely
‘Subsystems
(AffectionWindow empty1)
(AffectionMouse empty2)
(AffectionKeyboard empty3)’
• Relevant bindings include
empty1 :: TVar [a0] (bound at examples/example00.hs:39:3)
|
43 | (AffectionWindow empty1)
| ^^^^^^
I have never encountered any error like this and I'm at my wits end.
Hopefully somebody in here knows the solution.
Thanks for your time!

Related

Confusing type missmatch error in nested do blocks

I'm trying to write an interpreter for a simple embedded scripting language.
The core of it is the eval function, which has the following signature.
type EvalState = () --for later
type EvalResult = State EvalState (IO (Either T.Text Value))
eval :: Expr -> EvalResult
The result type is like this because it is statefull, eval should be able todo IO and it can fail.
There are two datatypes: Expressions and Values and eval converts expression to values. For simple expressions, literals of primitive data types, it's implemented like this:
ok :: Value -> EvalResult
ok val = state (return . Right $ val,)
eval :: Expr -> EvalResult
eval (StrLit t) = ok (StrVal t)
eval (SymLit t) = ok (SymbolVal t)
eval (IntLit i) = ok (IntVal i)
eval (FloatLit f) = ok (FloatVal f)
My problem now is implementing eval for the list literal. Currently my code looks like this:
eval (ListLit elems) = do
elems <- mapM eval elems
do
opts <- sequence elems
return $ fmap (Right . ListVal . V.fromList) (sequence opts)
And this produces the following error:
/home/felix/git/vmail/src/Interpreter/EvalAst.hs:37:13: error:
• Couldn't match type ‘IO’
with ‘StateT EvalState Data.Functor.Identity.Identity’
Expected: StateT
EvalState Data.Functor.Identity.Identity [Either T.Text Value]
Actual: IO [Either T.Text Value]
• In a stmt of a 'do' block: opts <- sequence elems
In a stmt of a 'do' block:
do opts <- sequence elems
fmap (Right . ListVal . V.fromList) (sequence opts)
In the expression:
do elems <- mapM eval elems
do opts <- sequence elems
fmap (Right . ListVal . V.fromList) (sequence opts)
|
37 | opts <- sequence elems
| ^^^^^^^^^^^^^^
My problem is that I'm not understanding this error. My thinking goes like this: the first do puts me in the State monad, so I shold be able to "extract" the results from mapM eval elems which I expect to be [ IO (Either ...) ] The next do should put me in the IO monad (because that's the next inner type in the result type), so I should be able to extract IO values, and as far as I understood it, sequence elems should give me a IO [ Either ... ] which I can then extract, so what is my mistake?
Monad do not compose, in general. I think you are being bit by this.
In general, if m and n are monads, we can not write m (n a) for a monadic action that mixes the features of m and n. In general, it could even fail to be a monad.
In your case, you are using something like State A (IO B) hoping to be able to access the state of type A and still do IO, but that's not the case.
Indeed, by definition we have (up to some wrappers):
State a b = a -> (a,b)
| | |-- result
| |-- new state
|-- old state
which in your case State A (IO B) it becomes
State A (IO B) = A -> (A, IO B)
| | |-- result
| |-- new state
|-- old state
Here, we can see that the new state must be generated without doing IO at all! This is a computation which forces the new state and the IO effects to be completely separated. It effectively behaves as if we had two separate functions
A -> A -- state update, no IO here
A -> IO B -- IO depending on the old state
Likely, that's not what you actually want. That is probably something like
A -> IO (A, B)
allowing the new state to be generated after IO is done.
To obtain that type, you can not nest monads, but you need a monad transformer like StateT:
StateT A IO B = A -> IO (A, B)
This is a monad. You will probably need to use lift or liftIO to convert IO actions to this type, but it should behave as desired.

Event Drive with Haskell - error of typing and managing object state

I am trying to use an architecture of Events in Haskell based on this example (that works perfectly):
https://wiki.haskell.org/Real_World_Applications/Event_Driven_Applications
And I try to apply this code to a more complex example. The idea is mutate an object (let's say Domain) in the most natural way to do it in Haskell :
data Domain =
Domain (String, World)
... and execute several commands to World and change it (or got a message in the first parameter of tuple).
With World a "class" of type:
data World = World {loc :: String, descLlocs :: [(String,String)]} deriving (Show)
But when a EventLook is launched, and for example
dmUpdate :: Domain -> Event -> Domain
dmUpdate (Domain v) (EventLook) = do
let msg = fst v
let newWorld = snd v
-- Maybe IO Action !? Is possible !?
return Domain (msg, newWorld)
dmUpdate dm _ = dm
I got this error (that with my point of view, "Domain (msg, newWorld)", is of type: Domain, no!? (I also tried to return (msg, newWorld) without success).
baseEventDomainProgram.hs:30:35: error:
• Couldn't match type ‘(String, World) -> Domain’ with ‘Domain’
Expected type: (String, World) -> Domain
Actual type: (String, World) -> (String, World) -> Domain
• The function ‘return’ is applied to two arguments,
but its type ‘((String, World) -> Domain)
-> (String, World) -> (String, World) -> Domain’
has only three
In a stmt of a 'do' block: return Domain (msg, newWorld)
In the expression:
do let msg = fst v
let newWorld = snd v
return Domain (msg, newWorld)
|
30 | return Domain (msg, newWorld)
Therefore, my idea is just pass newWorld to compute the new state (change the data of the object).
I can add this toy example.
import System.IO
data Event =
EventExit -- User wants to exit
| EventLook
| EventAdd Int
deriving(Eq,Show)
data World = World {loc :: String, descLlocs :: [(String,String)]} deriving (Show)
theWorld = World {loc = "living-room", descLlocs = [("living-room","you are in the living-room. a wizard is snoring loudly on the couch.")
,("garden","you are in a beautiful garden. there is a well in front of you.")
, ("attic", "you are in the attic. there is a giant welding torch in the corner.")]}
data Domain =
Domain (String, World)
dmUpdate :: Domain -> Event -> Domain
dmUpdate (Domain v) (EventLook) = do
let msg = fst v
let newWorld = snd v
-- Maybe IO Action !?
return (Domain (msg, newWorld))
dmUpdate dm _ = dm
uiUpdate :: Domain -> IO [Event]
uiUpdate (Domain v) = do
putStrLn $ "WORLD> " ++ show (fst v)
input <- read'
if input == ":quit" then
return [EventExit]
else
return [EventLook]
run :: Domain -> [Event] -> IO ()
run dm [] = do
events <- uiUpdate dm
run dm events
run _ (EventExit:_) =
return ()
run dm (e:es) =
run (dmUpdate dm e) es
read' :: IO String
read' = putStr "WORLD> "
>> hFlush stdout
>> getLine
main :: IO ()
main = run (Domain ("",theWorld)) []
Thanks in advance!
EDITED :
As pointed by #jpmarinier the code shoud return only one argument so: "return (Domain (msg,newWorld))" should be better. So I edited the code shared with this correct sentence.
But in this case I got two errors:
baseEventDomainProgram.hs:31:17: error:
• Couldn't match expected type ‘m Domain’ with actual type ‘Domain’
• In the expression: dm
In an equation for ‘dmUpdate’: dmUpdate dm _ = dm
• Relevant bindings include
dmUpdate :: Domain -> Event -> m Domain
(bound at baseEventDomainProgram.hs:26:1)
|
31 | dmUpdate dm _ = dm
| ^^
baseEventDomainProgram.hs:51:8: error:
• Couldn't match expected type ‘Domain’
with actual type ‘m0 Domain’
• In the first argument of ‘run’, namely ‘(dmUpdate dm e)’
In the expression: run (dmUpdate dm e) es
In an equation for ‘run’: run dm (e : es) = run (dmUpdate dm e) es
|
51 | run (dmUpdate dm e) es
| ^^^^^^^^^^^^^
In fact, you're almost done.
There is a slight change required to have the body of function dmUpdate agree with its type:
import System.IO
data Event =
EventExit -- User wants to exit
| EventLook
| EventAdd Int
deriving(Eq,Show)
data World = World {loc :: String, descLlocs :: [(String,String)]} deriving (Show)
theWorld = World {loc = "living-room",
descLlocs = [("living-room", "you are in the living-room. a wizard is snoring loudly on the couch.")
, ("garden", "you are in a beautiful garden. there is a well in front of you.")
, ("attic", "you are in the attic. there is a giant welding torch in the corner.")]}
data Domain = Domain (String, World)
-- plain version:
dmUpdate :: Domain -> Event -> Domain
dmUpdate (Domain v) (EventLook) =
let msg = fst v
newWorld = snd v
in
(Domain (msg, newWorld))
dmUpdate dm _ = dm
Note that the return function is gone, as you do NOT return a monadic type here.
Alternatively, this would be the monadic version (unused in the rest of your code):
-- monadic version:
dmUpdateM :: Monad m => Domain -> Event -> m Domain
dmUpdateM (Domain v) (EventLook) =
do
let msg = fst v
newWorld = snd v
return (Domain (msg, newWorld))
dmUpdateM dm _ = return dm
Side note: in Haskell, the word return is rather unfortunate. I think it should be called wrap instead of return. Unlike in imperative languages, return is an ordinary function which plays no role in control flow. Its type is:
return :: Monad m => a -> m a
So returnis just a component of Haskell monadic API. For example, in the context of the list monad, expression (return 42) evaluates to just [42].
The rest of the code compiles OK:
uiUpdate :: Domain -> IO [Event]
uiUpdate (Domain v) = do
putStrLn $ "WORLD> " ++ show (fst v)
input <- read'
if input == ":quit" then
return [EventExit]
else
return [EventLook]
run :: Domain -> [Event] -> IO ()
run dm [] = do
events <- uiUpdate dm
run dm events
run _ (EventExit:_) =
return ()
run dm (e:es) =
run (dmUpdate dm e) es
read' :: IO String
read' = putStr "WORLD> "
>> hFlush stdout
>> getLine
main :: IO ()
main = run (Domain ("",theWorld)) []
Testing:
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.8.4
$
$ ghc q68226112.hs -o ./q68226112.x
Linking ./q68226112.x ...
$
$ ./q68226112.x
WORLD> ""
WORLD> ba
WORLD> ""
WORLD> :quit
$

Best way to fix couldn't match expected type when your giving the right type (probably not)

Sorry for asking another question about this dice game :).
I've got the following code:
--type GepakteStenen = [Steen]
--data Tactiek = Tactiek
-- {tactiekPakken ::GepakteStenen -> Worp -> IO Steen
--, tactiekDoorgaan ::GepakteStenen -> IO Bool
-- }
tactiekUitv :: Worp -> GepakteStenen -> Predicaat -> IO(Steen,Bool)
tactiekUitv w g p = do s <- (tactiekPakken g w)
let gs = g ++ filter (s==) w
if (magStoppen p gs) then
return (s,tactiekDoorgaan gs)
else
return (s,True)
Where i get this error message:
Couldn't match expected type `Tactiek' with actual type `[Steen]'
Why is it that i am giving the right type to my "tactiekDoorgaan" function, it wants me to create that a Tactiek of that?
This declaration
data Tactiek = Tactiek
{ tactiekPakken :: GepakteStenen -> Worp -> IO Steen
...
makes tactiekPakken a three argument function
tactiekPakken :: Tactiek -> GepakteStenen -> Worp -> IO Steen
-- ^^^^^^^ --
because there is a first, implicit argument having your record type. this might be confusing at first, but after all, we can not access a record field without having a record value at hand.
In your code, you do not provide the implicit Tactiek argument
do s <- (tactiekPakken g w)
-- ^^^^^^^^^^^^^^ --

How to assign a value from the IO monad to a RankNType qualified constructor

(UPDATED)
I have made an interface using a Free Monad to a generic data store. I want to place the specific interpreter (:: DataStore a -> IO a) chosen by the user at run time into a state monad along with some other information. I cannot seem to put anything into this field in the data structure.
How do I put a value into a field defined as a higher rank type?
Below is a minimum example:
{-# LANGUAGE RankNTypes, DeriveFunctor #-}
data ProgramState = PS { -- line 3
[...]
, storageInterface :: (forall a. DataStore a -> IO a)
}
data DataStoreF next =
Create Asset ( String -> next)
| Read String ( Asset -> next)
| Update Asset ( Bool -> next)
| UpdateAll [Asset] ( Bool -> next)
| [...]
deriving Functor
type DataStore = Free DataStoreF
runMemory :: (IORef (Map String Asset)) -> DataStore a -> IO a
runMemory ms (Pure a) = return a
runMemory ms (Free Create asset next) = [...]
runMemory ms (Free Read str next) = [...]
[...]
pickStorageInterface :: IO (DataStore a -> IO a)
pickStorageInterface = do
opts <- parseOptions
case (storage opts) of
MemoryStorage ->
ms <- readAssetsFromDisk
return $ runMemory ms
SomeOtherStorage -> [...]
restOfProgram :: StateT ProgramState IO
restOfProgram = [...]
main = do
si <- pickStorageInterface
let programState = PS { storageInterface = si} -- line 21
evalState restOfProgram programState
When I try to do this GHC complains that:
Main.hs: << Line 21 >>
Couldn't match type `a0' with `a'
because type variable `a' would escape its scope
This (rigid, skolem) type variable is bound by
a type expected by the context: DataStore a -> IO a
at Main.hs <<line 3>>
Expected type: DataStore a -> IO a
Actual type: DataStore a0 -> IO a0
In the `storageInterface' field of a record
[...]
UPDATE
My original minimal example was to minimal. Some further experimentation shows that the problem arises when I need to load the interface in an the IO monad so I can read the command line options. I've updated the example to include that issue. Knowing this I may be able to code around it.
Interesting GHCI tells me that the results of a function of type IO (DataStore a -> IO a) is DataStore GHC.Prim.Any -> IO GHC.Prim.Any which is not what I expected.
The issue here is that
pickStorageInterface :: forall a. IO (DataStore a -> IO a)
while we would need the (impredicative) type
pickStorageInterface :: IO (forall a. DataStore a -> IO a)
for the code above to work. Alas, the impredicative types are in a sad state now in GHC, and are best to be avoided.
You can work around that using a newtype wrapper around the universally quantified type:
newtype SI = SI { runSI :: forall a. DataStore a -> IO a }
pickStorageInterface :: IO SI
pickStorageInterface = do
opts <- parseOptions
case (storage opts) of
MemoryStorage ->
ms <- readAssetsFromDisk
return $ SI $ runMemory ms
...
main = do
si <- pickStorageInterface
let programState = PS { storageInterface = runSI si}
...

How to correctly qualify types for working with the (transformed) ST and random monads

Here is my code:
...
import System.Random ( RandomGen, next, split )
import qualified Data.Array.MArray as MAI
import Data.Array.ST.Safe( STUArray )
import Control.Monad.ST.Safe(ST)
import qualified Control.Monad.Random as CMR
import Control.Monad.Trans.Class( lift )
data GraphEdgeYaml = GraphEdgeYaml {
specie1:: NodeName,
specie2 :: NodeName,
sign :: Int,
speed :: Int
}
type LinksSTA s = STUArray s Int GraphEdgeYaml
-- Change a simple link
swapLink :: RandomGen g =>
LinksSTA s
-> g
-> ST s g
swapLink graph generator =
let
swap_op :: CMR.RandT g (ST s) ()
swap_op = do
(low_limit, high_limit) <- lift $ MAI.getBounds graph
idx_value <- CMR.getRandomR (low_limit, high_limit)
return ()
in do
(_, new_generator) <- CMR.runRandT swap_op generator
return new_generator
and here is the error message that I get:
hs/SignMatrixBuild/Randomize.hs:43:26:
Could not deduce (RandomGen g1)
arising from a use of `CMR.getRandomR'
from the context (RandomGen g)
bound by the type signature for
swapLink :: RandomGen g => LinksSTA s -> g -> ST s g
at hs/SignMatrixBuild/Randomize.hs:(38,1)-(47,28)
Possible fix:
add (RandomGen g1) to the context of
the type signature for swap_op :: CMR.RandT g1 (ST s1) ()
or the type signature for
swapLink :: RandomGen g => LinksSTA s -> g -> ST s g
In a stmt of a 'do' block:
idx_value <- CMR.getRandomR (low_limit, high_limit)
In the expression:
do { (low_limit, high_limit) <- lift $ MAI.getBounds graph;
idx_value <- CMR.getRandomR (low_limit, high_limit);
return () }
In an equation for `swap_op':
swap_op
= do { (low_limit, high_limit) <- lift $ MAI.getBounds graph;
idx_value <- CMR.getRandomR (low_limit, high_limit);
return () }
How do I fix this?
One way to fix it is to bring the type variables s and g into scope using the ScopedTypeVariables extension, the other is to simply omit the local type signature on swap_op.
If the local signature is omitted, the type can be inferred -- that leaves, however, the problem of the constraint
MAI.MArray (STUArray s) GraphEdgeYaml (ST s)
that is needed. There are two options,
change the array type to STArray
use STUArrays indeed
If you change the array type to STArray, no constraint is needed (since there is an instance MArray (STArray s) e (ST s) that covers all element types), and without the local type signature it compiles without problems.
If you want to keep the STUArrays, the function can only be used where an instance is in scope. It is best, usually, to provide such instances either where the class is defined (not an option here) or where the type is defined (that would be this module).
So then you should write an
instance MAI.MArray (STUArray s) GraphEdgeYaml (ST s)
in that module, with that instance, the constraint would be fulfilled and need not be placed on the function. Note, however, that writing such an instance is not trivial.
Alternatively, you could add the constraint to the signature and load off the burden of defining an (orphan) instance to the user of swapLink.
I don't know what a NodeName is, but whether GraphEdgeYaml is an unboxable type seems doubtful. I would therefore recommend switching to STArrays.

Resources