首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Netwire 5号的Kleisli Arrow?

Netwire 5号的Kleisli Arrow?
EN

Stack Overflow用户
提问于 2015-09-23 17:33:22
回答 1查看 248关注 0票数 3

我正在尝试创建一个使用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,但是我找不到一个合适的构造函数来制作这样的电线。

我是新的玻璃钢和箭头,没有太多的编程在Haskell,所以这可能不是最好的方式来实现图形输出。如果我从一开始就错了,请告诉我。

一些SDL功能与以下相关:

代码语言:javascript
复制
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接口以进行测试。

代码语言:javascript
复制
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
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-09-29 02:42:01

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

整个代码都是用识字的Haskell编写的,所以只需复制它并运行。

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

代码语言:javascript
复制
import Control.Wire
import Control.Arrow
import Prelude hiding ((.), id)

现在,这是制造克莱斯里电线的核心。假设您有一个具有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)的函数。请注意,左侧抑制电线,而右侧激活它,因此内部部分是Either () b而不是Either b ()。实际上,如果您尝试后者,一个模糊的编译错误会告诉您这是错误的。

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

代码语言:javascript
复制
liftToEither :: (Monad m) => (a -> m b) -> (a -> m (Either () b))
liftToEither f = \a -> (f a >>= return . Right)

现在,我们可以做一条克莱斯里电线:

代码语言:javascript
复制
mkKleisli :: (Monad m, Monoid e) => (a -> m b) -> Wire s e m a b
mkKleisli f = mkGen_ $ \a -> (f a >>= return . Right)

那么,让我们试试典型的“你好,世界”的电线!

代码语言:javascript
复制
helloWire :: Wire s () IO () ()
helloWire = pure "hello, world" >>> mkKleisli putStrLn

现在来了一个主要的功能来说明如何驱动电线。请注意,与来自the Control.Wire.Run库的the Control.Wire.Run中的liftIO源相比,liftIO是没有用的:外部程序不知道这些连接在内部是如何工作的。它只是踩着电线,忽略了里面的东西。Maybe这个Just意味着比使用Nothing更好的组合关于Kleisli电线?(没有双关的意思!)

代码语言:javascript
复制
main = go clockSession_ helloWire
    where
      go s w = do
        (ds, s') <- stepSession s
        (mx, w') <- stepWire w ds (Right ())
        go s' w'

现在来了密码。不幸的是,StackOverflow对识字的Haskell不太好.

代码语言:javascript
复制
{-# 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'

更新

多亏了立方的灵感。liftToEither实际上可以写成,你猜的,liftM

代码语言:javascript
复制
liftToEither f = \a -> liftM Right $ f a
mkKleisli f = mkGen_ $ \a -> liftM Right $ f a
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32745934

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档