我目前正在尝试创建一个小Haskell EDSL来描述电子电路并模拟它们。我已经听说过熔岩,但我并没有真正读到它,因为我想要进行全面冒险"。我最终得到了一个类型类Bit
,代表了你可以进行逻辑运算的任何东西,并使用列表来表示时间(第n个元素是第n个循环的值)。
我的寄存器实现为reg input = ground : input
,其中ground是表示逻辑false的Bit b => b
。只要我不在自身上循环寄存器,这种实现就非常有效。事实上,当写alternate = not <$> (reg alternate)
之类的东西时,我最终会构建not . not . not ... . not $ ground
的巨大thunk而不是
iterate' f x = x `seq` x : (iterate' f (f x))
alternate = iterate' not ground
本来会更有效率。虽然,这不适用于不属于循环的寄存器,并且我不想将1)循环寄存器2)的语法分开而不是循环寄存器。
我有点卡在那里,但感觉这是一个非常普遍的问题,因此有人可能会有解决方案。
提前致谢!
答案 0 :(得分:3)
如果您想尝试使用monadic方法,您可能需要查看LeventErkök的论文Value Recursion in Monadic Computations(第1.2章)和MonadFix package。
巧合的是,Erkök通过展示Monadic电路描述和解释解决方案来激发他的研究。正如他所展示的,使用Monad,如果您需要不同的行为(例如简单地绘制而不是分析电路),则不必更改电路描述。这可能也非常有用,具体取决于您希望实施的程度。
从他的论文中了解解决方案:
halfAdd :: Circuit m => Sig Bool -> Sig Bool -> m (Sig Bool, Sig Bool)
halfAdd s1 s2 = do
sum <- xor s1 s2
carry <- and s1 s2
return (sum, carry)
看起来非常整洁(详见第1.2章),其中&gt;&gt; = 和返回定义您的电路模型 m ,Sig是一个值列表,并使用 xor ,和等通用操作来转换类型类,....
更重要的是,他讨论了Monads中的递归(反馈循环)并指出你不想循环遍历电路元素,可能复制它们(并在你的情况下创建thunk)但是在实际的信号值上,因此需要值递归。
因此,具有反馈回路的非门看起来像:
toggle sig = mdo
inp <- inv out
out <- delay "False" False inp -- Used to define the initial signal value on the wire
return out
请注意使用 mdo 而不是do,它允许(相互)递归定义。 (您必须启用RecursiveDo扩展才能获得对mdo的支持)。
在幕后,这将被翻译成
toggle sig = do
mfix (\out -> do
inp <- inv out
out' <- delay "False" False inp
return out')
(有关符号的更多信息,另请参阅https://www.haskell.org/haskellwiki/MonadFix。)
请注意,此解决方案以及mfix(针对您的特定电路模型/ monad)的定义不会生成任何中间门。 您可以在论文中找到所有设计和实施细节,以及有关有趣法律的更多材料。
也许这对你有所帮助。
编辑:提供更详细的答案。