gpt4 book ai didi

haskell - Netwire 5 中的 Kleisli Arrow?

转载 作者:行者123 更新时间:2023-12-02 17:13:51 24 4
gpt4 key购买 nike

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

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

我是 FRP 和 Arrows 的新手,并且没有在 Haskell 中进行过很多编程,因此这可能不是实现图形输出的最佳方法。如果我从一开始就错了,请告诉我。

一些相关的 SDL 函数:

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 接口(interface)进行测试

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 线。我还演示了如何驱动电线,结果非常简单。

整个代码是用 Literate Haskell 编写的,因此只需复制并运行即可。

首先,有一些 Netwire 5 库的导入

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

现在,这就是制作 Kleisli Wire 的核心。假设你有一个类型为 a -> m b 的函数需要将其提升到电线中。现在,请注意 mkGen_ 具有类型mkGen_::Monad m => (a -> m (Either e b)) -> Wire s e m a b

因此,要使用 a -> m b 制作一条线,我们首先需要获取一个函数类型为a -> m(Either () b)。请注意,Left 抑制了电线,而 Right 激活它,所以内部部分是 Either () b 而不是b () 之一。实际上,如果您尝试后者,则会出现一个模糊的编译错误会告诉你以错误的方式得到这个。

要得到a -> m(Either () b),首先考虑如何得到m (Either () b) from m b,我们从 monad (mb),将其向右提升,然后返回到单子(monad) m。简而言之:mB >>= 返回 .对。。由于我们这里没有值“mB”,所以我们创建一个 lambda 表达式来获取 a -> m (Either () b):

liftToEither :: (Monad m) => (a -> m b) -> (a -> m (Either () b))
liftToEither f = \a -> (f a >>= return . Right)

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

mkKleisli :: (Monad m, Monoid e) => (a -> m b) -> Wire s e m a b
mkKleisli f = mkGen_ $ \a -> (f a >>= return . Right)

那么,让我们尝试一下规范的“hello, world”线路!

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

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

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 配合使用...

{-# 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:

liftToEither f = \a -> liftM Right $ f a
mkKleisli f = mkGen_ $ \a -> liftM Right $ f a

关于haskell - Netwire 5 中的 Kleisli Arrow?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32745934/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com