Netwire 5中的Kleisli Arrow? [英] Kleisli Arrow in Netwire 5?

查看:125
本文介绍了Netwire 5中的Kleisli Arrow?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图用Haskell + Netwire 5(+ SDL)创建一个游戏。现在我正在处理输出部分,在那里我想创建一些读取某些游戏状态的线路,并将SDL表面输出到屏幕上。然而,问题是SDL曲面包含在 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆