6

Haskell で次のようなデータ型がある場合:

data A ctx = A (forall a. ctx a => a -> a)

次に、特定のクラスの型の値に作用する関数をこのデータ型に入れることができます。

> A (+3) :: A Num
A (+3) :: A Num :: A Num

しかし、複数の制約を持つ関数をこのデータ型に入れることは可能ですか? 私はこれを試しました:

> :t ((+ 3) . succ)
((+ 3) . succ) :: (Enum c, Num c) => c -> c
> :t A ((+ 3) . succ) :: A (Enum,Num)
  Expecting one more argument to `Enum'
  In an expression type signature: A (Enum, Num)
  In the expression: A ((+ 3) . succ) :: A (Enum, Num)

したがって、これは機能しません。どうすればやりたいことができるのか、それとも不可能なのか? 「ダミー」データ型と型ファミリを使用することが 1 つの解決策であることはわかっていますが、複雑なため別の方法がある場合はそうしたくありません。また、この例では succ の代わりに (+1) を使用することもできましたが、1 つのクラスだけに変換できない複雑なケースもあります。

4

1 に答える 1

1

複数のクラスを 1 つに結合するという考えを一般化する 1 つの方法を見つけました。かなり使いにくいですが、機能します。

{-# LANGUAGE ConstraintKinds       #-}
{-# LANGUAGE EmptyDataDecls        #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE KindSignatures        #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverlappingInstances  #-}
{-# LANGUAGE RankNTypes            #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TypeOperators         #-}
{-# LANGUAGE UndecidableInstances  #-}
module Test where

import GHC.Exts

data (:>+<:) ctx1 ctx2 a where
  C'C :: (ctx1 a, ctx2 a) => (ctx1 :>+<: ctx2) a

data (:++:) (ctx1 :: * -> Constraint) (ctx2 :: * -> Constraint) = C

class Ctx2 c1 c2 a where
  useCtx :: c1 :++: c2 -> a -> ((c1 :>+<: c2) a -> b) -> b

instance (c1 a, c2 a) => Ctx2 c1 c2 a where
  useCtx _ a f = f C'C

fC :: (Ctx2 Num Enum a) => a -> a
fC a = useCtx (C :: Num :++: Enum) a $ \C'C  -> 3 + succ a

data A ctx = A 
  { call :: forall a. ctx a => a -> a
  }

main = print $ call (A fC :: A (Ctx2 Num Enum)) 3

制約エイリアスを使用して Ctx3、Ctx4、... を定義できるはずです。

于 2013-06-22T21:28:23.900 に答える