Netwire 5中的Kleisli Arrow? [英] Kleisli Arrow in Netwire 5?
问题描述
IO
monad中,所以任何创建这种曲面的函数都必须有类型 a - > IO b
。当然, arr
不会从 a - >构建 Wire
m b
。然而,由于线的类型签名是(Monad m,Monoid e)=> Wire semab
,它看起来很像Kleisi Arrow,但是我找不到合适的构造函数来制作这样的线。 我是新手FRP和箭头,并且没有在Haskell中编程太多,所以这可能不是实现图形输出的最佳方式。如果我从一开始就错了,请告诉我。
一些与SDL相关的功能:
createRGBSurfaceEndian :: [SurfaceFlag] - > Int - > Int - > Int - > IO Surface
fillRect :: Surface - >也许Rect - >像素 - > IO Bool
blitSurface :: Surface - >也许Rect - >表面 - >也许Rect - > IO Bool
flip :: Surface - > IO()
更新1
代码类型检查,但现在我试图将其与SDL进行交互以进行测试。
wTestOutput ::(Monoid e)=> Wire s e IO()SDL.Surface
wTestOutput = mkGen_ $ \ a - > (makeSurf a>> = return。Right)
其中
makeSurf :: a - > IO SDL.Surface
makeSurf _ = do
s< - SDL.createRGBSurfaceEndian [SDL.SWSurface] 800 600 32
SDL.fillRect s(Just testRect)(SDL.Pixel 0xFF000000)
return s
testRect = SDL.Rect 100 100 0 0
现在,在玩过Arrows之后,我将使用函数 putStrLn
来回答我自己的问题
。它的类型是 String - > IO()
,即
a - > m b
,所以该方法应该推广到所有Kleisli线。我还说明了如何驱动线路,结果非常简单。
整个代码都是用Literate Haskell编写的,所以只需复制并运行即可。
首先,Netwire 5库有一些导入
import Control。 Wire
导入Control.Arrow
前导隐藏((。),id)
<现在,这是制作Kleisli Wire的核心。假设您有一个
函数,并且类型为 a - > m b
需要提升到一根线。现在,
注意到 mkGen _
的类型为
mkGen_ :: Monad m => (a→m(或者e b))→>因此,要从
a - >> m b
,我们首先需要得到一个函数
,类型为 a - > m(或者()b)
。请注意,Left禁止导线
,而Right激活它,所以内部是 Either()b
,而不是
b()
。实际上,如果你尝试后者,一个不明确的编译错误
会告诉你以错误的方式得到它。
获得 a - > m(或者()b)
,首先考虑如何从
m(或者()b)
> mb ,我们从monad中提取值(m
b),将其提升到Right,然后返回到monad m。简而言之:
mB>> = return。右
。由于我们在这里没有值mB,所以我们
使lambda表达式得到 a - > m(或者()b)
:
liftToEither ::(Monad m)=> (a - > m b) - > (a - > m(E()b))
liftToEither f = \ a - > (fa>> = return。右)
现在,我们可以制作一根Kleisli线:
mkKleisli ::(Monad m,Monoid e)=> (a - > m b) - > Wire s e m a b
mkKleisli f = mkGen_ $ \ a - > (fa>> = return。Right)
所以,让我们试试规范的hello,world wire!
helloWire :: Wire s()IO()()
helloWire = purehello,world >>> mkKleisli putStrLn
现在介绍如何驱动电线的主要功能。请注意
与 Control.Wire.Run
中源自 testWire
Netwire库,没有使用liftIO:外部程序
对内部线路的工作方式一无所知。它只是步骤
的电线而忽略其中的内容。 也许
这个只是
表示比使用更好的
组合 c>关于Kleisli电线? (没有双关语意思!)
main = go clockSession_ helloWire
其中
go sw = do
(ds,s')< - stepSession s
(mx,w')< - stepWire w ds(Right())
go s'w'
现在来看代码。不幸的是,StackOverflow在Literate Haskell中效果不佳...
{ - #LANGUAGE箭头# - }
模块Main其中
导入Control.Wire
导入Control.Monad
导入Control.Arrow
导入前导隐藏((。),id)
mkKleisli ::(Monad m,Monoid e)=> (a - > m b) - > Wire s e m a b
mkKleisli f = mkGen_ $ \ a - > liftM Right $ f a
helloWire :: Wire s()IO()()
helloWire = purehello,world>>> mkKleisli putStrLn
main = go clockSession_helloWire
其中
go sw = do
(ds,s')< - stepSession s
(mx, w')< - stepWire w ds(Right())
go s'w'
更新
感谢Cubic的灵感。 liftToEither
实际上可以写入,你猜猜它, liftM
:
liftToEither f = \ a - > liftM Right $ f a
mkKleisli f = mkGen_ $ \ a - > liftM Right $ f a
I am trying to create a game using Haskell + Netwire 5 (+ SDL). Now I am working on the output part, where I would like to create wires that read in some game state and output the SDL surfaces to be blitted on screen.
However, the problem is that SDL surfaces are contained in IO
monad, so any function that creates such surfaces must have type a -> IO b
. Of course, arr
does not construct a Wire
from a -> m b
. However, since the type signature of a wire is (Monad m, Monoid e) => Wire s e m a b
, it looks quite like a Kleisi Arrow, but I cannot find a suitable constructor for making such a wire.
I am new to FRP and Arrows, and have not programmed a lot in Haskell, so this may not be the best way to implement the graphics output. If I am wrong from the beginning, please let me know.
Some SDL functions related:
createRGBSurfaceEndian :: [SurfaceFlag] -> Int -> Int -> Int -> IO Surface
fillRect :: Surface -> Maybe Rect -> Pixel -> IO Bool
blitSurface :: Surface -> Maybe Rect -> Surface -> Maybe Rect -> IO Bool
flip :: Surface -> IO ()
Update 1
This code type checks, but now I am trying to interface it with SDL for testing
wTestOutput :: (Monoid e) => Wire s e IO () SDL.Surface
wTestOutput = mkGen_ $ \a -> (makeSurf a >>= return . Right)
where
makeSurf :: a -> IO SDL.Surface
makeSurf _ = do
s <- SDL.createRGBSurfaceEndian [SDL.SWSurface] 800 600 32
SDL.fillRect s (Just testRect) (SDL.Pixel 0xFF000000)
return s
testRect = SDL.Rect 100 100 0 0
Now, after playing around with Arrows, I will answer my own question
using the function putStrLn
. It has type String -> IO ()
, which is
a -> m b
, so the method should generalize to all Kleisli wires. I also illustrate how to drive the wire, and the result is amazingly simple.
The entire code is written in Literate Haskell, so just copy it and run.
First, there are some imports for the Netwire 5 library
import Control.Wire
import Control.Arrow
import Prelude hiding ((.), id)
Now, this is the core of making a Kleisli Wire. Assume you have a
function with type a -> m b
that needs to be lifted into a wire. Now,
notice that mkGen_
has type
mkGen_ :: Monad m => (a -> m (Either e b)) -> Wire s e m a b
So, to make a wire out of a -> m b
, we first need to get a function
with type a -> m (Either () b)
. Notice that Left inhibits the wire,
while Right activates it, so the inner part is Either () b
instead of
Either b ()
. Actually, if you try the latter, an obscure compile error
will tell you get this in the wrong way.
To get a -> m (Either () b)
, first consider how to get
m (Either () b)
from m b
, we extract the value from the monad (m
b), lift it to Right, then return to the monad m. In short:
mB >>= return . Right
. Since we don't have the value "mB" here, we
make a lambda expression to get a -> m (Either () b)
:
liftToEither :: (Monad m) => (a -> m b) -> (a -> m (Either () b))
liftToEither f = \a -> (f a >>= return . Right)
Now, we can make a Kleisli wire:
mkKleisli :: (Monad m, Monoid e) => (a -> m b) -> Wire s e m a b
mkKleisli f = mkGen_ $ \a -> (f a >>= return . Right)
So, let's try the canonical "hello, world" wire!
helloWire :: Wire s () IO () ()
helloWire = pure "hello, world" >>> mkKleisli putStrLn
Now comes the main function to illustrate how to drive the wire. Note
that comparing to the source of testWire
in the Control.Wire.Run
from the Netwire library, there is no use of liftIO: the outer program
knows nothing about how the wires work internally. It merely steps the
wires ignoring what is in it. Maybe
this Just
means better
composition than using Nothing
about Kleisli Wires? (No pun intended!)
main = go clockSession_ helloWire
where
go s w = do
(ds, s') <- stepSession s
(mx, w') <- stepWire w ds (Right ())
go s' w'
Now here comes the code. Unfortunately StackOverflow does not work quite well with Literate Haskell...
{-# LANGUAGE Arrows #-}
module Main where
import Control.Wire
import Control.Monad
import Control.Arrow
import Prelude hiding ((.), id)
mkKleisli :: (Monad m, Monoid e) => (a -> m b) -> Wire s e m a b
mkKleisli f = mkGen_ $ \a -> liftM Right $ f a
helloWire :: Wire s () IO () ()
helloWire = pure "hello, world" >>> mkKleisli putStrLn
main = go clockSession_ helloWire
where
go s w = do
(ds, s') <- stepSession s
(mx, w') <- stepWire w ds (Right ())
go s' w'
Update
Thanks to Cubic's inspiration. liftToEither
can actually be written in, you guess it, liftM
:
liftToEither f = \a -> liftM Right $ f a
mkKleisli f = mkGen_ $ \a -> liftM Right $ f a
这篇关于Netwire 5中的Kleisli Arrow?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!