我最近有想法建立一个计算计算经过的绑定数量的monad。我想出了以下内容:
newtype K a = K { runK :: Int -> (a, Int) }
instance Functor K where
fmap f k = K $ \s ->
let (iv, s') = runK k s
in (f iv, s')
instance Applicative K where
pure x = K $ \s -> (x, s)
f <*> b = K $ \s ->
let (f', s') = runK f s
(ih, s'') = runK b s'
in (f' ih, s'')
instance Monad K where
return x = K $ \s -> (x, s)
a >>= f = K $ \s ->
let (iv, s') = runK a s
in runK (f iv) (s' + 1)
我后来意识到这只是State
monad并不是很有趣。然后,我有了尝试构建一个计算“连接”的箭头的想法。在这里,我有点难过。
newtype L a b = L { runL :: Int -> (a -> b, Int) }
instance Category L where
id = arr Prelude.id
b . a = L $ \s ->
let (f1, s') = runL a s
(f2, s'') = runL b s'
in (f2 Prelude.. f1, s'' + 1)
instance Arrow L where
arr f = L $ \s -> (f, s + 1)
first a = L $ \s ->
let (f1, s') = runL a s
in (\(x, y) -> (f1 x, y), s')
我提出了上面的计算连接数,但不计算计算经过的连接数。建议?
答案 0 :(得分:5)
(这与Daniel Wagner的观察结果非常相似,但我认为我会更友好。)
你应该冥想像monad法则那样具有法律的含义。这是一个可以变得微妙和复杂的主题,但您可以应用的一个非常简单的经验法则是:
x = y
,则该类别的客户不应该分开x
和y
。f
,以便:
x = y
; f x /= f y
现在这不是一个绝对的规则。以排序为例;合并排序和冒泡排序都是稳定的排序算法,因此它们具有相同的功能&#34;从某种意义上说,除了查看输入和输出之外,你无法区分它们。但是如果你使用&#34;侧面频道&#34;像时间一样,你仍然可以将它们区分开来,但是当我们询问某些代码是否符合某项法律时,我们通常会将其排除在外。
现在,当我们将此应用于您的K
类型时,问题是您可以编写一个程序来区分monad法则所说的两个表达式,这些表达式应该是难以区分的。例如,在documentation for the Monad
class中它表示:
此外,
Monad
和Applicative
操作应如下所示:pure = return (<*>) = ap
我们可以通过这样的QuickCheck测试轻松驳斥其中的第二个:
import Control.Monad (ap)
import Test.QuickCheck
import Test.QuickCheck.Function
-- Snipped your code for `K`
prop_ap :: Fun Int Int -> Int -> Bool
prop_ap f x = runK applicative 0 == runK monadic 0
where applicative = pure (apply f) <*> pure x
monadic = return (apply f) `ap` return x
立即进行测试失败:
>>> quickCheck prop_ap
*** Failed! Falsifiable (after 1 test and 2 shrinks):
{_->0}
0
这里的原因是您的Monad
实例正在执行+1
,但您的Applicative
实例并未执行此类操作。因此,我们可以区分等效计算,但其中一个使用<*>
构建,另一个使用ap
构建。
答案 1 :(得分:2)
正如其他人所描述的那样,你无法统计&#34;连接&#34;例如绑定或箭头绑定操作,因为它们不会满足monad / arrow法则。
但是,可以做的是创建一个箭头,您可以在其中明确地为构建块指定大小,然后计算此类电路的大小。举个例子: { - #LANGUAGE箭头# - }
import Control.Arrow
import qualified Control.Category as C
data A a b c = A { runA :: a b c, sizeA :: !Int }
在这里,我们定义了一个包装现有箭头并添加其大小的箭头变换器。
instance (C.Category a) => C.Category (A a) where
id = A C.id 0
(A a1 s1) . (A a2 s2) = A (a1 C.. a2) (s1 + s2)
instance (Arrow a) => Arrow (A a) where
arr f = A (arr f) 0
first (A f s) = A (first f) s
instance (ArrowChoice a) => ArrowChoice (A a) where
left (A f s) = A (left f) s
-- instance (Arrow a) => ArrowTransformer A a where
-- lift a = A a 0
默认大小必须始终为0才能满足法律要求。例如,id
的大小必须为x . id === x
,这要归功于法律arr id === id
,我们认为arr f
的大小必须为0等。
但我们可以定义一个自定义函数,为给定的箭头指定给定的大小:
sized :: Int -> a b c -> A a b c
sized = flip A
举一个例子,让我们构建一些A (->)
类型的箭头。也就是说,底层箭头只是函数。
-- * Example (adapted from https://wiki.haskell.org/Arrow_tutorial)
-- | Both 'f' and 'g' are constructed to have a size of 1.
f, g :: A (->) Int Int
f = sized 1 $ arr (`div` 2)
g = sized 1 $ arr (\x -> x*3 + 1)
h :: A (->) Int Int
h = proc x -> do
fx <- f -< x
gx <- g -< x
returnA -< (fx + gx)
您可以h
作为runA h 5
运行。但您也可以按sizeA h
来衡量其大小,它会按预期返回2。请注意,您不需要运行箭头来获取其大小。你可以把它想象成一个电路,你不需要实际上电电路来查看它的大小。
请注意,我们无法为monad执行此操作,因此我们无法拥有实例ArrowChoice A
。在monad中我们可以计算下一个&#34;效果&#34;从之前的结果来看,这意味着我们永远不能在没有实际运行monadic计算的情况下计算大小。