遵循this post中出色答案的指导,我试图获得不使用箭头符号的ArrowLoop
的有效示例。在完全理解箭头在引擎盖下的工作方式之前,我不习惯使用箭头表示法。话虽这么说,但我基于对Arrows的(有限)理解而构建了一个小程序,该程序应该可以工作。但是,它最终以可怕的<<loop>>
异常终止:
module Main where
import Control.Wire
import FRP.Netwire
farr :: SimpleWire (Int, Float) (String, Float)
farr = let
fn :: Int -> Float -> ((String, Float), SimpleWire (Int, Float) (String, Float))
fn i f = (("f+i: " ++ (show (fromIntegral i + f)), f + 0.1), loopFn)
loopFn :: SimpleWire (Int, Float) (String, Float)
loopFn = mkSFN $ \(i, f) -> fn i f
in
mkSFN $ \(i, _) -> fn i 0.0
main :: IO ()
main = do
let sess = clockSession_ :: Session IO (Timed NominalDiffTime ())
(ts, sess2) <- stepSession sess
let wire = loop farr
(Right s, wire2) = runIdentity $ stepWire wire ts (Right 0)
putStrLn ("s: " ++ s)
(ts2, _) <- stepSession sess2
let (Right s2, _) = runIdentity $ stepWire wire2 ts (Right 1)
putStrLn ("s2: " ++ s2)
我的直觉告诉我,当您不向循环提供初始值时,通常会出现
<<loop>>
异常。我不是用fn i 0.0
行完成了吗?输出不同意:$ ./test
s: f+i: 0.0
test.exe: <<loop>>
有人知道我在做什么错吗?
最佳答案
混淆的主要点似乎是ArrowLoop
和mfix
之间的整体关系。对于未启动的用户,fix
是给定函数的finds the fixed point的函数:
fix :: (a -> a) -> a
fix f = let x = f x in x
mfix
是此函数的单例扩展,其类型签名毫不奇怪:mfix :: (a -> m a) -> m a
那么,这与
ArrowLoop
有什么关系?好吧,Netwire的ArrowLoop
实例在传递的导线的第二个参数上运行mfix
。换句话说,考虑loop
的类型签名:loop :: a (b, d) (c, d) -> a b c
在Netwire中,
ArrowLoop
的实例为:instance MonadFix m => ArrowLoop (Wire s e m)
这意味着与导线一起使用时,
loop
函数的类型为:loop :: MonadFix m => Wire s e m (b, d) (c, d) -> Wire s e m b c
由于
loop
不接受d
类型的初始参数,因此,这意味着无法通过导线初始化任何类型的常规“循环”。从中获取值的唯一方法是继续将输出用作输入,直到找到终止条件为止,这类似于fix
的工作方式。作为loop
的参数传递的连线实际上不会跨步超过一次,因为stepWire
一遍又一遍地通过不同的输入应用于同一根连线。仅当导线实际产生固定值时,函数才会执行步骤并产生另一根导线(其行为与第一个相同)。为了完整起见,以下是我本人的直觉代码,该代码应被称为
loop
,应该如何工作:semiLoop :: (Monad m, Monoid s, Monoid e) => c -> Wire s e m (a, c) (b, c) -> Wire s e m a b
semiLoop initialValue loopWire = let
runLoop :: (Monad m, Monoid s, Monoid e) =>
Wire s e m (a, c) (b, c) -> s -> a -> c -> m (Either e b, Wire s e m a b)
runLoop wire ts ipt x = do
(result, nextWire) <- stepWire wire ts (Right (ipt, x))
case result of
Left i -> return (Left i, mkEmpty)
Right (value, nextX) ->
return (Right value, mkGen $ \ts' ipt' -> runLoop nextWire ts' ipt' nextX)
in
mkGen $ \ts input -> runLoop loopWire ts input initialValue
编辑
在由Petr提供wonderful answer之后,
semiLoop
组合器对于防止delay
组合器发散至关重要。 loop
仅在上述循环的delay
部分中使用下一个值的延迟之间创建一个单值缓冲区。因此,上面的mfix
的定义相同:semiLoop :: (MonadFix m, Monoid s, Monoid e) =>
c -> Wire s e m (a, c) (b, c) -> Wire s e m a b
semiLoop initialValue loopWire = loop $ second (delay initialValue) >>> loopWire