可能无法匹配类型变量
我正在尝试使用相当特定的体系结构(有限状态机)创建电报机器人。例如,可能会有(1)状态可以运行某些功能,该状态将返回其他状态或(2)状态,该状态将将bot移至其他具有不同状态的机器。
因此,我创建了类似的类别和两种类型:
{-# LANGUAGE DeriveAnyClass, GADTs #-}
module Main where
class (Eq a) => TeleClass a
type BotView a = String -> a
data TeleState a where
TeleState :: TeleClass a => a -> BotView a -> TeleState a
TeleMachineChange :: (TeleClass a, TeleClass b) => a -> TeleMachine b -> TeleState a
data TeleMachine a = TeleMachine
{ defaultState :: a
, stateMachine :: [TeleState a]
}
然后GHC允许我创建一些看起来完全像我想要的FSM:
data MainStates = MAIN_MENU | TASK_CHOOSING | CHANGE_VIEW | MODER_VIEW
deriving (TeleClass, Eq)
data AdminStates = ADMIN_MENU | PUBLISH_TASK
deriving (TeleClass, Eq)
data ModerStates = MODER_MENU | EDIT_TASK
deriving (TeleClass, Eq)
fMainMenu _ = TASK_CHOOSING
fChooseTask _ = CHANGE_VIEW
fAdminMenu _ = PUBLISH_TASK
fPublishTask _ = ADMIN_MENU
fModerMenu _ = EDIT_TASK
fEditTask _ = MODER_MENU
moderMachine = TeleMachine
{ defaultState = MODER_MENU
, stateMachine =
[ TeleState MODER_MENU fModerMenu
, TeleState EDIT_TASK fEditTask
]
}
adminMachine = TeleMachine
{ defaultState = ADMIN_MENU
, stateMachine =
[ TeleState ADMIN_MENU fAdminMenu
, TeleState PUBLISH_TASK fPublishTask
]
}
mainMachine = TeleMachine
{ defaultState = MAIN_MENU
, stateMachine =
[ TeleState MAIN_MENU fMainMenu
, TeleState TASK_CHOOSING fChooseTask
, TeleMachineChange CHANGE_VIEW adminMachine
, TeleMachineChange MODER_VIEW moderMachine
]
}
然后将一些状态应用于FSM,我添加了此功能:
data MatchResult a b = Func a | Machine b | None
matchState :: (TeleClass a, TeleClass c) =>
[TeleState a] -> a -> MatchResult (BotView a) (TeleMachine c)
matchState (TeleState mState fFunc : stateMachine) state
| mState == state = Func fFunc
| otherwise = matchState stateMachine state
matchState (TeleMachineChange mState mMachine : stateMachine) state
| mState == state = Machine mMachine
| otherwise = matchState stateMachine state
matchState [] _ = None
编译器抱怨它无法与“ C”类型匹配:
• Couldn't match type ‘b’ with ‘c’
‘b’ is a rigid type variable bound by
a pattern with constructor:
TeleMachineChange :: forall a b.
(TeleClass a, TeleClass b) =>
a -> TeleMachine b -> TeleState a,
in an equation for ‘matchState’
at example.hs:70:13-45
‘c’ is a rigid type variable bound by
the type signature for:
matchState :: forall a c.
(TeleClass a, TeleClass c) =>
[TeleState a] -> a -> MatchResult (BotView a) (TeleMachine c)
at example.hs:(65,1)-(66,65)
Expected type: MatchResult (BotView a) (TeleMachine c)
Actual type: MatchResult (BotView a) (TeleMachine b)
• In the expression: Machine mMachine
In an equation for ‘matchState’:
matchState (TeleMachineChange mState mMachine : stateMachine) state
| mState == state = Machine mMachine
| otherwise = matchState stateMachine state
• Relevant bindings include
mMachine :: TeleMachine b (bound at example.hs:70:38)
matchState :: [TeleState a]
-> a -> MatchResult (BotView a) (TeleMachine c)
(bound at example.hs:67:1)
|
71 | | mState == state = Machine mMachine
| ^^^^^^^^^^^^^^^^
我尝试使用存在量的量化和量化限制扩展,但无法解决此问题。现在,我不知道这是否可行,可以在Haskell之类的语言中实现类似的内容。
I'm trying to create telegram bot with quite specific architecture (Finite-state machines). For example, there can be (1) state that will run some function, that will return other state or (2) state that will move bot into some other machine with different states.
So I created class and two types that look something like that:
{-# LANGUAGE DeriveAnyClass, GADTs #-}
module Main where
class (Eq a) => TeleClass a
type BotView a = String -> a
data TeleState a where
TeleState :: TeleClass a => a -> BotView a -> TeleState a
TeleMachineChange :: (TeleClass a, TeleClass b) => a -> TeleMachine b -> TeleState a
data TeleMachine a = TeleMachine
{ defaultState :: a
, stateMachine :: [TeleState a]
}
And then GHC allows me to create some FSMs that look exactly like I want:
data MainStates = MAIN_MENU | TASK_CHOOSING | CHANGE_VIEW | MODER_VIEW
deriving (TeleClass, Eq)
data AdminStates = ADMIN_MENU | PUBLISH_TASK
deriving (TeleClass, Eq)
data ModerStates = MODER_MENU | EDIT_TASK
deriving (TeleClass, Eq)
fMainMenu _ = TASK_CHOOSING
fChooseTask _ = CHANGE_VIEW
fAdminMenu _ = PUBLISH_TASK
fPublishTask _ = ADMIN_MENU
fModerMenu _ = EDIT_TASK
fEditTask _ = MODER_MENU
moderMachine = TeleMachine
{ defaultState = MODER_MENU
, stateMachine =
[ TeleState MODER_MENU fModerMenu
, TeleState EDIT_TASK fEditTask
]
}
adminMachine = TeleMachine
{ defaultState = ADMIN_MENU
, stateMachine =
[ TeleState ADMIN_MENU fAdminMenu
, TeleState PUBLISH_TASK fPublishTask
]
}
mainMachine = TeleMachine
{ defaultState = MAIN_MENU
, stateMachine =
[ TeleState MAIN_MENU fMainMenu
, TeleState TASK_CHOOSING fChooseTask
, TeleMachineChange CHANGE_VIEW adminMachine
, TeleMachineChange MODER_VIEW moderMachine
]
}
And then to apply some state to FSM I added this function:
data MatchResult a b = Func a | Machine b | None
matchState :: (TeleClass a, TeleClass c) =>
[TeleState a] -> a -> MatchResult (BotView a) (TeleMachine c)
matchState (TeleState mState fFunc : stateMachine) state
| mState == state = Func fFunc
| otherwise = matchState stateMachine state
matchState (TeleMachineChange mState mMachine : stateMachine) state
| mState == state = Machine mMachine
| otherwise = matchState stateMachine state
matchState [] _ = None
And compiler complained that it couldn't match type 'b' with 'c':
• Couldn't match type ‘b’ with ‘c’
‘b’ is a rigid type variable bound by
a pattern with constructor:
TeleMachineChange :: forall a b.
(TeleClass a, TeleClass b) =>
a -> TeleMachine b -> TeleState a,
in an equation for ‘matchState’
at example.hs:70:13-45
‘c’ is a rigid type variable bound by
the type signature for:
matchState :: forall a c.
(TeleClass a, TeleClass c) =>
[TeleState a] -> a -> MatchResult (BotView a) (TeleMachine c)
at example.hs:(65,1)-(66,65)
Expected type: MatchResult (BotView a) (TeleMachine c)
Actual type: MatchResult (BotView a) (TeleMachine b)
• In the expression: Machine mMachine
In an equation for ‘matchState’:
matchState (TeleMachineChange mState mMachine : stateMachine) state
| mState == state = Machine mMachine
| otherwise = matchState stateMachine state
• Relevant bindings include
mMachine :: TeleMachine b (bound at example.hs:70:38)
matchState :: [TeleState a]
-> a -> MatchResult (BotView a) (TeleMachine c)
(bound at example.hs:67:1)
|
71 | | mState == state = Machine mMachine
| ^^^^^^^^^^^^^^^^
I tried to use ExistentialQuantification and QuantifiedConstraints extensions, but couldn't fix this problem with them. Right now I don't know if this is viable idea to implement something like this in language like haskell.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
签名
...这只是
表达的速记,即对于
c
呼叫者选择的任何类型,此功能将能够从输入列表中获取合适的telemachine
。这似乎是一件艰难的事情。我认为您要表达的是“好的,我会尝试在其中找到一台匹配的机器,但我不知道它的类型是什么”。确实,这确实是存在类型
haskell不允许使用存在类型的功能,但是您可以使
matchResult
类型存在,与telemachinechange
telemachine
的构造函数是存在的:或者,您可以使用持续通话风格的
matchstate
伪造存在类型:The signature
...which is just shorthand for
expresses that for whatever type
c
the caller picks, this function will be able to procure a suitableTeleMachine
from the input list. It seems a tough thing to do.I think what you're trying to express is instead “ok, I'll try to find a matching machine in there, but I don't know what its type is going to be”. That is indeed an existential type
Haskell doesn't allow functions with existential type, but you could make the
MatchResult
type existential, similary to how theTeleMachineChange
constructor ofTeleMachine
is existential:Alternatively, you can fake an existential type for
matchState
with continuation-passing style: