不将类型识别为转换器堆栈中 monad 的关联类型

Not recognizing a type as an associated type of a monad inside a transformer stack

我一般都在玩 OpenGL,特别是 Haskell 名为 GPipe 的库。我有一个 monad 转换器堆栈,底部有 IO,然后是库中的 ContextT 转换器,然后是 StateT,因为需要一些状态,最后是 newtype Processor,因为simple type 会为这样的堆栈产生可怕的错误消息。这是一般的想法。但是,下面的代码没有进行类型检查:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE TemplateHaskell #-}
module Main where

import Control.Lens
import Control.Monad.State
import Control.Monad.Trans
import Control.Monad.Except

import qualified "GPipe" Graphics.GPipe as GP
import qualified "GPipe-GLFW" Graphics.GPipe.Context.GLFW as GLFW

---- State and Processor types ----

class ArtState os as | as -> os where
    event :: GP.ContextHandler ctx => as -> Processor ctx os (as, Maybe e)
    present :: GP.ContextHandler ctx => as -> Processor ctx os as
    window :: Lens' as (WindowType os)

data ProgramState = ProgramState

newtype GP.ContextHandler ctx => Processor ctx os a = Processor {
    runProcessor :: StateT ProgramState (GP.ContextT ctx os IO) a
}

---- MenuArt things ----

type WindowType os = GP.Window os GP.RGBFloat GP.Depth

data MenuArt os = MenuArt {
    _maWindow :: WindowType os
}

makeLenses ''MenuArt

instance ArtState os (MenuArt os) where
    event ms = Processor $ return (ms, Nothing)
    present ms = Processor $ return ms
    window = maWindow

initMenuArt :: (ArtState os a, GP.ContextHandler ctx) =>
       Maybe a 
    -> Processor ctx os (Either String (MenuArt os))
initMenuArt Nothing = Processor $ do
    win <- lift $ GP.newWindow (GP.WindowFormatColorDepth GP.RGB8 GP.Depth16)
                               (GLFW.defaultWindowConfig "foobar")
    return $ Right $ MenuArt {
        _maWindow = win
    }
initMenuArt (Just from) = Processor $ do
    return $ Right $ MenuArt {
        _maWindow = from ^. window
    }

---- events ----

data UserEvent = CloseWindow

报错信息如下:

/tmp/testing/app/Main.hs:49:33: error:
    • Couldn't match expected type ‘GP.WindowParameters ctx’
                  with actual type ‘GLFW.WindowConfig’
    • In the second argument of ‘GP.newWindow’, namely
        ‘(GLFW.defaultWindowConfig "foobar")’
      In the second argument of ‘($)’, namely
        ‘GP.newWindow
           (GP.WindowFormatColorDepth GP.RGB8 GP.Depth16)
           (GLFW.defaultWindowConfig "foobar")’
      In a stmt of a 'do' block:
        win <- lift
                 $ GP.newWindow
                     (GP.WindowFormatColorDepth GP.RGB8 GP.Depth16)
                     (GLFW.defaultWindowConfig "foobar")
    • Relevant bindings include
        initMenuArt :: Maybe a
                       -> Processor ctx os (Either String (MenuArt os))
          (bound at app/Main.hs:47:1)
   |
49 |                                (GLFW.defaultWindowConfig "foobar")
   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

据我所知,newWindow 期望 WindowParameters ctx 作为它的第二个参数,它是 ContextHandler class 的关联类型。但是出于某种原因,编译器看不到此堆栈的 GLFW.WindowConfigWindowParameters。从堆栈中扔掉 StateTProcessor(就像我正在学习的教程一样)有效,编译:

main :: IO ()
main = do
  GP.runContextT GLFW.defaultHandleConfig $ do
    win <- GP.newWindow (GP.WindowFormatColor GP.RGB8) (GLFW.defaultWindowConfig "foobar")
    return ()
  return ()

我做错了什么,但不知道是什么。

initMenuArt 正在使用 GLFW.defaultWindowConfig,这是一个 GLFW 函数。

GPipe 定义了一个由 ctx 类型参数化的接口,GPipe-GLFW 通过实例化 ctxGLFW.Handle.[=19= 来实现该接口]

因此 initMenuArt 应该相应地专门化:

initMenuArt
    :: (ArtState os a)
    => Maybe a 
    -> Processor GLFW.Handle os (Either String (MenuArt os))