参数类型类

时间:2016-03-17 16:12:44

标签: haskell typeclass generic-programming

是否有办法声明提供/提供函数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

1 个答案:

答案 0 :(得分:4)

你的问题似乎有点模糊,但这听起来像是一个具有功能依赖性的多参数类型类的潜在用例。

{-# LANGUAGE  MultiParamTypeClasses
            , FunctionalDependencies #-}

class Monad m => MyClass d m | m -> d where
  getDs :: m [d]

然后MyClass d m表示mMonadgetDs可用于生成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]

这基本上是一回事,但是

  1. 任何撰写MyClass实例的人都必须包含一行,例如type Available IO = Int

  2. Available类型设置约束的任何人都需要在约束中使用Available,并且需要FlexibleContexts(不是什么大不了的事)。

  3. 类型系列提供对相关类型的访问。

  4. 类型系列在GHC Core(AKA System FC)中表示,因此在某些方面它们的表现比功能依赖性更好。

  5. 1(特别)和2可以说是类型族方法的缺点; 3和4是好处。这在很大程度上归结为品味问题。