Haskell 的事件驱动器 - 输入和管理对象状态时出错
Event Drive with Haskell - error of typing and managing object state
我正在尝试根据此示例(完美运行)在 Haskell 中使用事件架构:
https://wiki.haskell.org/Real_World_Applications/Event_Driven_Applications
并且我尝试将此代码应用于更复杂的示例。这个想法是以最自然的方式在 Haskell 中改变一个对象(比如说域):
data Domain =
Domain (String, World)
... 并向 World 执行多个命令并更改它(或者在元组的第一个参数中得到一条消息)。
With World a "class" of type:
data World = World {loc :: String, descLlocs :: [(String,String)]} deriving (Show)
但是当启动 EventLook 时,例如
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
我得到了这个错误(以我的观点,“Domain (msg, newWorld)”的类型是:Domain,不!?(我也尝试 return (msg, newWorld)没有成功)。
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)
因此,我的想法是通过newWorld来计算新的状态(改变对象的数据)。
我可以添加这个玩具示例。
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)) []
提前致谢!
已编辑:
正如@jpmarinier 所指出的,代码应该 return 只有一个参数,因此:“return (Domain (msg,newWorld))” 应该更好。所以我编辑了与这个正确句子共享的代码。
但在这种情况下我得到了两个错误:
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
| ^^^^^^^^^^^^^
事实上,您几乎完成了。
要使函数体 dmUpdate
与其类型一致,需要稍作更改:
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
请注意,return
函数已消失,因为您在这里没有 return 单子类型。
或者,这将是 monadic 版本(在您的其余代码中未使用):
-- 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
旁注:在Haskell中,return这个词是相当不幸的。我觉得应该叫wrap
而不是return
。与命令式语言不同,return
是一个普通函数,在控制流中不起作用。它的类型是:
return :: Monad m => a -> m a
所以 return
只是 Haskell monadic API 的一个组成部分。例如,在列表 monad 的上下文中,表达式 (return 42)
的计算结果只是 [42]
.
其余代码编译成功:
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)) []
测试:
$ 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
$
我正在尝试根据此示例(完美运行)在 Haskell 中使用事件架构: https://wiki.haskell.org/Real_World_Applications/Event_Driven_Applications
并且我尝试将此代码应用于更复杂的示例。这个想法是以最自然的方式在 Haskell 中改变一个对象(比如说域):
data Domain =
Domain (String, World)
... 并向 World 执行多个命令并更改它(或者在元组的第一个参数中得到一条消息)。
With World a "class" of type:
data World = World {loc :: String, descLlocs :: [(String,String)]} deriving (Show)
但是当启动 EventLook 时,例如
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
我得到了这个错误(以我的观点,“Domain (msg, newWorld)”的类型是:Domain,不!?(我也尝试 return (msg, newWorld)没有成功)。
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)
因此,我的想法是通过newWorld来计算新的状态(改变对象的数据)。 我可以添加这个玩具示例。
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)) []
提前致谢!
已编辑: 正如@jpmarinier 所指出的,代码应该 return 只有一个参数,因此:“return (Domain (msg,newWorld))” 应该更好。所以我编辑了与这个正确句子共享的代码。
但在这种情况下我得到了两个错误:
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
| ^^^^^^^^^^^^^
事实上,您几乎完成了。
要使函数体 dmUpdate
与其类型一致,需要稍作更改:
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
请注意,return
函数已消失,因为您在这里没有 return 单子类型。
或者,这将是 monadic 版本(在您的其余代码中未使用):
-- 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
旁注:在Haskell中,return这个词是相当不幸的。我觉得应该叫wrap
而不是return
。与命令式语言不同,return
是一个普通函数,在控制流中不起作用。它的类型是:
return :: Monad m => a -> m a
所以 return
只是 Haskell monadic API 的一个组成部分。例如,在列表 monad 的上下文中,表达式 (return 42)
的计算结果只是 [42]
.
其余代码编译成功:
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)) []
测试:
$ 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
$