Haskell 在可组合的自由DSL中处理不明确的类型

Haskell 在可组合的自由DSL中处理不明确的类型,haskell,free-monad,Haskell,Free Monad,我正在构建两个DSL,它们应该基于“免费单子”和“按单点数据类型”进行组合,使用免费和compdata包(精神上类似于) 虽然这适用于一些简单的DSL,但我还是停留在一个具有类型参数的DSL上,在构造函数/命令不依赖于此类型参数的情况下,这会导致GHC产生不明确的类型参数错误 为了澄清,这里有一些代码: {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeOperators #-}

我正在构建两个DSL,它们应该基于“免费单子”和“按单点数据类型”进行组合,使用免费和compdata包(精神上类似于)

虽然这适用于一些简单的DSL,但我还是停留在一个具有类型参数的DSL上,在构造函数/命令不依赖于此类型参数的情况下,这会导致GHC产生不明确的类型参数错误

为了澄清,这里有一些代码:

{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeOperators #-}

module DSL where

import Data.Comp
import Control.Monad.Free

type Index = Int

data DSL a next = Read Index (a -> next)
                | Write a (Index -> next)
                | GetLastIndex (Index -> next)
    deriving (Functor)

read :: (Functor f, DSL a :<: f, MonadFree f m) => Index -> m a
read idx = liftF (inj (Read idx id))

write :: (Functor f, DSL a :<: f, MonadFree f m) => a -> m Index
write a = liftF (inj (Write a id))

-- This works
getLastIndex' :: MonadFree (DSL a) m => m Index
getLastIndex' = liftF (GetLastIndex id)

-- This doesn't:
--
--     Could not deduce (Data.Comp.Ops.Subsume
--                         (compdata-0.10:Data.Comp.SubsumeCommon.ComprEmb
--                            (Data.Comp.Ops.Elem (DSL a0) f))
--                         (DSL a0)
--                         f)
--     from the context (Functor f, DSL a :<: f, MonadFree f m)
--       bound by the type signature for
--                  getLastIndex :: (Functor f, DSL a :<: f, MonadFree f m) => m Index
--       at simple.hs:30:17-66
--     The type variable ‘a0’ is ambiguous
--     In the ambiguity check for the type signature for ‘getLastIndex’:
--       getLastIndex :: forall (m :: * -> *) (f :: * -> *) a.
--                       (Functor f, DSL a :<: f, MonadFree f m) =>
--                       m Index
--     To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
--     In the type signature for ‘getLastIndex’:
--       getLastIndex :: (Functor f, DSL a :<: f, MonadFree f m) => m Index

getLastIndex :: (Functor f, DSL a :<: f, MonadFree f m) => m Index
-- getLastIndex = liftF (inj (GetLastIndex id))
getLastIndex = _
{-#语言派生函子#-}
{-#语言灵活语境#-}
{-#语言类型运算符{-}
模块DSL在哪里
导入数据.Comp
进口管制,不含单子
类型索引=Int
数据DSL a next=读取索引(a->next)
|写一个(索引->下一步)
|GetLastIndex(索引->下一步)
派生(函子)
阅读::(函子f,DSL a:索引->MA
读取idx=liftF(inj(读取idx id))
写入::(函子f,DSL a:a->m索引
写入a=liftF(inj(写入id))
--这很有效
getLastIndex':MonadFree(DSL a)m=>m索引
getLastIndex'=liftF(getLastIndex id)
--这并不是:
--
--无法推断(Data.Comp.Ops.Subsume)
--(compdata-0.10:Data.Comp.SubsumeCommon.compemb
--(Data.Comp.Ops.Elem(DSL a0)f)
--(DSL a0)
--(f)
--从上下文(函子f,DSL a:*)(f::*->*)a。
--(函子f,DSL a:
--m指数
--要延迟歧义检查以使用站点,请启用AllowAmbigUstypes
--在“getLastIndex”的类型签名中:
--getLastIndex::(函子f,DSL a:m索引
getLastIndex::(函子f,DSL a:m索引
--getLastIndex=liftF(inj(getLastIndex id))
getLastIndex=_
正如GHC所暗示的那样,试图通过启用AllowAmbigUstypes扩展来实现这一点并没有让我得到任何进一步的帮助。我尝试在类型签名中添加一些关于所有a样式的内容,但没有成功


有什么办法可以让这种模式发挥作用吗?

这是一个相对著名的开放式和“点菜式”限制

简言之,如果我们有一个本身有一个或多个内部类型索引的
f
函子,那么对于包含该函子的开和,类型推断会受到很大影响

为了说明原因,假设我们有一个包含
DSL()
和a
DSL Int
。GHC必须为其中一个选择一个实例,但使用
getLastIndex
是不可能的,因为
a
参数在参数或返回类型中没有提及。GHC从上下文中基本上没有关于
a
的信息

这可以通过使用
Data.Proxy以一种有点笨拙的方式进行补救:

import Data.Proxy

getLastIndex ::
  forall a f m.
  (Functor f, DSL a :<: f, MonadFree f m)
  => Proxy a -> m Index
getLastIndex _ = liftF (inj (GetLastIndex id :: DSL a Index))
import Data.Proxy
getLastIndex::
对于所有a f m。
(函子f,DSL a:代理a->m索引
getLastIndex_u2;=liftF(inj(getLastIndex id::DSL a索引))
或者,如果我们要求开和中只有一个
DSL
,我们可以恢复良好的类型推断和明确性
涉及重写<代码>的类型级查找代码:我刚刚发现这正是Haskell所抱怨的,但是你的答案比我写的关于这个主题的两段要令人满意得多。令人敬畏地说,干杯。感谢你的彻底回答!解释为什么“DSL a”无法解决的部分是有意义的,一个d需要“某些”DSL层也有意义,但现在我无法使用示例代码定义“读”或“写”…@NicolasTrangez我编辑了我的链接示例。现在可以在那里找到
read
write
。我们在类型中使用
IxOf DSL f
而不是
a
参数。