Haskell状态机基于运行时值调用泛化函数的类型匹配问题求解
问题原因
你遇到的编译错误核心是Haskell要求case表达式的所有分支必须返回完全相同的类型:
- First分支返回的
Created1 inProcessKey类型为SState First FakeState Created - Second分支返回的
Created2 inProcessKey类型为SState Second FakeState Created
两个类型没有公共统一类型,同时你delegateEntity2的入参f默认是绑定到某一个固定类型t的处理函数,无法同时兼容两种不同t的SState输入,所以触发类型不匹配错误。
修复方案
有两种常用的实现方式可选:
方案1:在case分支内直接调用处理函数(最简单)
你不需要先把不同类型的SState取出来再统一传参,直接把f的调用放到每个case分支里即可,此时每个分支内的f会自动适配对应t的类型:
delegateEntity2 InProcess {..} f = do en <- S.get case en of First -> f $ Created1 inProcessKey Second -> f $ Created2 inProcessKey return $ Processed inProcessKey
同时你需要修改MetaMachine类中delegateEntity2的签名,开启RankNTypes扩展,让f支持处理任意符合SMachine t m约束的t类型:
首先在文件头加扩展声明:
{-# LANGUAGE RankNTypes #-}
然后修改MetaMachine类的delegateEntity2定义:
delegateEntity2 :: State m InProcess -> (forall t. SMachine t m => SState t m Created -> m (SState t m Processed)) -> m (State m Processed)
这个改动不需要调整其他逻辑就能直接编译通过。
方案2:用存在量化类型包装不同子状态机的状态
如果你确实需要先拿到统一包装的状态值再做后续处理,可以定义一个存在量化GADT隐藏类型参数t:
data SomeSState m s where SomeSState :: SMachine t m => SState t m s -> SomeSState m s
此时你原来的case分支可以统一返回SomeSState类型:
delegateEntity2 InProcess {..} f = do en <- S.get delegatedEntity <- case en of First -> return $ SomeSState $ Created1 inProcessKey Second -> return $ SomeSState $ Created2 inProcessKey case delegatedEntity of SomeSState s -> f s return $ Processed inProcessKey
这个方案同样需要开启RankNTypes扩展,并且修改delegateEntity2的签名和方案1一致。
设计优化建议
如果你的子状态机的状态结构差异不大,也可以考虑把子状态机的共性抽出来放到统一的类型里,用运行时的标签区分类型,避免过多的类型级编程带来的复杂度,更适合业务逻辑迭代频繁的场景。
内容的提问来源于stack exchange,提问作者Yarick
相关产品推荐
相关产品推荐

