Haskell:尝试让tapyclasses成为FSM的Rust dyn Trait



我是一个Haskell初学者,我正在尝试用Haskell制作一个有限状态机,就像Rob Pike在他关于Lexing和Go的会议上展示的那样。槽锈样dyn性状

就像这个

pub struct Something {
something: bool,
}
pub trait Saladable {
fn next(&self, state: Something) -> (Box<dyn Saladable>, Something);
}
struct A;
struct B;
impl Saladable for A {
fn next(&self, state: Something) -> (Box<dyn Saladable>, Something) {
match state.something {
true => (Box::new(A {}), Something { something: false }),
false => (Box::new(B {}), Something { something: false }),
}
}
}
impl Saladable for B {
fn next(&self, _state: Something) -> (Box<dyn Saladable>, Something) {
(Box::new(A {}), Something { something: true })
}
}

但是当试图将其转换为Haskell

{-# LANGUAGE AllowAmbiguousTypes #-}
module Main where
main = undefined
data Something = Something Bool
class Saladable m where
next :: Saladable f => Something -> (Something, f)
data A = A
data B = B
instance Saladable A where
next (Something True) = (Something False, A)
next (Something False) = (Something False, B)
instance Saladable B where
next _ = (Something True, A)

我得到这个错误

Test.hs:15:45: error:
• Couldn't match expected type ‘f’ with actual type ‘A’
‘f’ is a rigid type variable bound by
the type signature for:
next :: forall f. Saladable f => Something -> (Something, f)
at Test.hs:15:3-6
• In the expression: A
In the expression: (Something False, A)
In an equation for ‘next’:
next (Something True) = (Something False, A)
• Relevant bindings include
next :: Something -> (Something, f) (bound at Test.hs:15:3)
|
15 |   next (Something True) = (Something False, A)
|                                             ^
Test.hs:19:29: error:
• Couldn't match expected type ‘f’ with actual type ‘A’
‘f’ is a rigid type variable bound by
the type signature for:
next :: forall f. Saladable f => Something -> (Something, f)
at Test.hs:19:3-6
• In the expression: A
In the expression: (Something True, A)
In an equation for ‘next’: next _ = (Something True, A)
• Relevant bindings include
next :: Something -> (Something, f) (bound at Test.hs:19:3)
|
19 |   next _ = (Something True, A)
|  

为什么我喜欢是一个为什么会发生这种情况的想法,以及我如何做才能复制Rust的这种行为。

我认为这是对Rust代码最直接的翻译:

{-# LANGUAGE ExistentialQuantification #-}
data Something = Something Bool
data SomeSaladable = forall a. Saladable a => SomeSaladable a
class Saladable a where
next :: (a, Something) -> (SomeSaladable, Something)
data A = A
data B = B
instance Saladable A where
next (_, Something True) = (SomeSaladable A, Something False)
next (_, Something False) = (SomeSaladable B, Something False)
instance Saladable B where
next _ = (SomeSaladable A, Something True)

这里的SomeSaladable包含一个存在量化的类型,更多信息请参阅本维基手册页面。

但实际上,这是Haskell中的反模式。注意,这个next函数现在不能再被链接了。您必须打开生成的SomeSaladable,所以这使用起来很痛苦。

一个常见的解决方案可以通过问:如果你知道你唯一能做的事情与结果Saladable是运行next函数,那么你为什么不只是存储它?如果你这样做,那么你会得到这样的内容:

data Something = Something Bool
data Saladable = Saladable (Something -> (Saladable, Something))
next :: (Saladable, Something) -> (Saladable, Something)
next (Saladable f, x) = f x
a, b :: Saladable
a = Saladable ((Something x) ->
case x of
False -> (a, Something False)
True -> (b, Something False))
b = Saladable (_ -> (a, Something True))

最新更新