参数类型类
Parametric typeclasses
有没有一种方法可以根据类型参数 d
声明 Conf
的泛型参数化类型 offers/provides 函数 frames
,例如
{-# 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
.
您的问题似乎有点含糊,但听起来像是具有函数依赖性的多参数类型class的潜在用例。
{-# 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
。对于每个 m
,只有一个 MyClass
的实例声明,并且 class 必须确定 d
是什么。所以你可以写一个像
这样的实例
instance MyClass Int IO where ...
(这将是仅允许IO
),但不是像
这样的软弱无力的东西
instance MyClass d IO where ...
因为后者没有确定d
.
您可能会觉得我为 MyClass
选择的参数顺序有点奇怪。这种疯狂有一些方法。主要原因是MyClass
可以部分应用。将它部分应用于 m
并不是很有用,因为这会留下一个只能由一个可能的参数满足的约束。另一方面,将其部分应用于 d
可能会有用,因为对于 d
的给定选择,可能有 m
的多个选择。因此给出 {-# 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 核心(又名系统 FC)中表达,因此在某些方面它们比函数依赖表现得更好。
1(特别是)和 2 可以说是类型族方法的缺点; 3和4是优势。这在很大程度上归结为品味问题。
有没有一种方法可以根据类型参数 d
声明 Conf
的泛型参数化类型 offers/provides 函数 frames
,例如
{-# 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
.
您的问题似乎有点含糊,但听起来像是具有函数依赖性的多参数类型class的潜在用例。
{-# 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
。对于每个 m
,只有一个 MyClass
的实例声明,并且 class 必须确定 d
是什么。所以你可以写一个像
instance MyClass Int IO where ...
(这将是仅允许IO
),但不是像
instance MyClass d IO where ...
因为后者没有确定d
.
您可能会觉得我为 MyClass
选择的参数顺序有点奇怪。这种疯狂有一些方法。主要原因是MyClass
可以部分应用。将它部分应用于 m
并不是很有用,因为这会留下一个只能由一个可能的参数满足的约束。另一方面,将其部分应用于 d
可能会有用,因为对于 d
的给定选择,可能有 m
的多个选择。因此给出 {-# 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 核心(又名系统 FC)中表达,因此在某些方面它们比函数依赖表现得更好。
1(特别是)和 2 可以说是类型族方法的缺点; 3和4是优势。这在很大程度上归结为品味问题。