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

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

问题描述

我正在尝试使用 Haskell + Netwire 5 (+ SDL) 创建游戏.现在我正在处理输出部分,我想创建在某些游戏状态下读取的连线,并输出要在屏幕上进行 blitted 的 SDL 表面.

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.

然而,问题在于 SDL 表面包含在 IO monad 中,因此任何创建此类表面的函数都必须具有类型 a ->IO b.当然,arr 不会从 a -> 构造一个 Wirem b.然而,由于线路的类型签名是 (Monad m, Monoid e) =>Wire s e m a b,它看起来很像 Kleisi Arrow,但我找不到合适的构造器来制作这种电线.

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.

我是 FRP 和 Arrows 的新手,并没有用 Haskell 编写很多程序,所以这可能不是实现图形输出的最佳方式.如果我从一开始就错了,请告诉我.

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.

一些相关的 SDL 函数:

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 ()

更新 1

此代码类型检查,但现在我尝试将其与 SDL 接口以进行测试

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

推荐答案

现在,在玩弄 Arrows 之后,我将回答我自己的问题使用函数 putStrLn.它的类型为 String ->IO(),即<代码>a ->m b,因此该方法应该推广到所有 Kleisli 线.我还演示了如何驱动电线,结果非常简单.

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.

整个代码都是用 Literate Haskell 编写的,所以只需复制并运行即可.

The entire code is written in Literate Haskell, so just copy it and run.

首先,Netwire 5 库有一些导入

First, there are some imports for the Netwire 5 library

import Control.Wire
import Control.Arrow
import Prelude hiding ((.), id)

现在,这是制作 Kleisli Wire 的核心.假设你有一个类型为 a -> 的函数m b 需要被提升成线.现在,注意 mkGen_ 有类型mkGen_ :: Monad m =>(a -> m (e b)) ->线 s e m a b

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

所以,用 a 制作电线 ->m b,我们首先需要得到一个函数类型 a ->m (() b) .请注意, Left 禁止了连线,而 Right 激活它,所以内部是 Either () b 而不是要么是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.

获得 a ->m(Either()b),首先考虑如何得到m (Either () b) from m b,我们从monad (mb),将其提升到 Right,然后返回到 monad m.简而言之:mB >>= return .没错.由于我们这里没有值mB",我们制作一个 lambda 表达式以获得 a ->m (Either () b):

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)

现在,我们可以制作 Kleisli 线了:

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)

那么,让我们试试规范的hello, world"连线吧!

So, let's try the canonical "hello, world" wire!

helloWire :: Wire s () IO () ()
helloWire = pure "hello, world" >>> mkKleisli putStrLn

现在来说明如何驱动线的主要功能.笔记与 Control.Wire.RuntestWire 的源代码比较从 Netwire 库中,没有使用liftIO:外部程序对电线在内部如何工作一无所知.它只是步骤电线无视其中的内容.也许这个Just意味着更好与使用关于 Kleisli Wires 的 Nothing 相比?(没有双关语!)

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'

现在代码来了.不幸的是,StackOverflow 与 Literate Haskell 不能很好地工作...

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'

更新

感谢 Cubic 的灵感.liftToEither其实可以写进去,你猜对了,liftM:

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天全站免登陆