是否有办法声明提供/提供函数Conf
的通用参数化类型frames
,具体取决于类型参数d
,例如
{-# LANGUAGE GeneralizedNewtypeDeriving
, MultiParamTypeClasses
, FunctionalDependencies #-}
import Control.Applicative
import Control.Monad
import Control.Monad.Identity
import Control.Monad.Trans.Class
import Control.Monad.Trans.State
import Control.Monad.Trans.Except
data MyConf d = MyConf { frames :: [d] } -- my parametric type
-- define a class to expose the interface in monads
class ConfM d m | m -> d where
getFrames :: m [d]
-- wrap StateT to include MyConf and allow instance of ConfM
newtype MyStateT d m a = MyStateT { runMyStateT :: StateT (MyConf d) m a }
deriving (Functor, Applicative, Monad, MonadTrans)
-- expose the interface over selected transformers
instance Monad m => ConfM d (MyStateT d m) where
getFrames = MyStateT $ fmap frames get
instance (ConfM d m, Monad m) => ConfM d (ExceptT a m) where
getFrames = lift getFrames
这样就可以写出类似的内容:
-- hide the gory implementation details
type MyMonad d = ExceptT A (MyStateT d B) C
class SomeClass a
-- this is the desired goal:
-- to concisely write an 'algorithm' in MyMonad only once
-- but for all possible choices of d from SomeClass
example :: SomeClass d => MyMonad d
example = do
fs <- getFrames
-- do SomeClass stuff with d
return ()
-- assume Int is instance of SomeClass
instance SomeClass Int
-- give me an instance of the above generic 'algorithm'
exampleInt :: MyMonad Int
exampleInt = example
-- assuming for example
type A = ()
type B = Identity
type C = ()
在上面的代码中,我被困在:
test.hs:23:25:
Illegal instance declaration for ‘ConfM d (MyStateT d m)’
(All instance types must be of the form (T a1 ... an)
where a1 ... an are *distinct type variables*,
and each type variable appears at most once in the instance head.
Use FlexibleInstances if you want to disable this.)
In the instance declaration for ‘ConfM d (MyStateT d m)’
test.hs:26:38:
Illegal instance declaration for ‘ConfM d (ExceptT a m)’
(All instance types must be of the form (T a1 ... an)
where a1 ... an are *distinct type variables*,
and each type variable appears at most once in the instance head.
Use FlexibleInstances if you want to disable this.)
In the instance declaration for ‘ConfM d (ExceptT a m)’
附加FlexibleInstances
test.hs:27:14:
Illegal instance declaration for ‘ConfM d (ExceptT
The coverage condition fails in class ‘ConfM’
for functional dependency: ‘m -> d’
Reason: lhs type ‘ExceptT a m’ does not determine
Using UndecidableInstances might help
In the instance declaration for ‘ConfM d (ExceptT a
所以我需要UndecidableInstances
。
答案 0 :(得分:4)
你的问题似乎有点模糊,但这听起来像是一个具有功能依赖性的多参数类型类的潜在用例。
{-# LANGUAGE MultiParamTypeClasses
, FunctionalDependencies #-}
class Monad m => MyClass d m | m -> d where
getDs :: m [d]
然后MyClass d m
表示m
是Monad
,getDs
可用于生成m [d]
类型的值。功能依赖性的目的是指示m
确定 d
。每个MyClass
只有一个m
实例声明,该类必须确定d
是什么。所以你可以写一个像
instance MyClass Int IO where ...
(那将是{em>仅允许IO
的一个),但不是像“
instance MyClass d IO where ...
因为后者不确定d
。
您可能会发现我对MyClass
的参数顺序选择有点奇怪。这种疯狂有一些方法。其主要原因是MyClass
可以部分应用。将它部分地应用于m
并不太有用,因为它留下了一个只能通过一个可能的参数来满足的约束。另一方面,将其部分应用于d
可能很有用,因为对于给定的m
选项,可能有多个d
选项。因此给出{-# LANGUAGE ConstraintKinds #-}
,
type MakesInts = MyClass Int
是一个潜在有用的约束。我相信使用此订单可能也有助于在某些情况下避免使用UndecidableInstances
,但我不确定。
提到的其他替代方法是使用相关类型系列。
{-# LANGUAGE TypeFamilies #-}
class Monad m => MyClass m where
type Available m
getDs :: m [Available m]
这基本上是一回事,但是
任何撰写MyClass
实例的人都必须包含一行,例如type Available IO = Int
。
对Available
类型设置约束的任何人都需要在约束中使用Available
,并且需要FlexibleContexts
(不是什么大不了的事)。
类型系列提供对相关类型的访问。
类型系列在GHC Core(AKA System FC)中表示,因此在某些方面它们的表现比功能依赖性更好。
1(特别)和2可以说是类型族方法的缺点; 3和4是好处。这在很大程度上归结为品味问题。