2011-06-25 26 views
38

言ってやるが、私はこのようなレコード属性を定義したい:haskellに 'Any'タイプがありますか?

data Attribute = Attribute {name :: String, value :: Any}

これは当然の有効なHaskellのコードではありません。しかし、基本的にどんなタイプでもいいと言うタイプの「Any」はありますか?または、型変数を唯一の方法で使用することですか?

data Attribute a = Attribute {name :: String, value :: a}

+5

'GHC.Prim.Any'を見てください。 –

+0

'name'に実際に型を持たせたい場合は、型変数を使うべきです。しかし、あなたは確信していますか?人の名前はどんなタイプでもなければなりませんか?おそらく(例えば)名前としての 'Int'値や名前としての' Bool'値は健全ではないでしょうか? – phynfo

+0

@phynfo、私が使ったサンプルは仮説です。私はそれをより一般的に改正しました。 – edwardw

答えて

65

一般に、Anyタイプはあまり役に立ちません。考えてみましょう:何かを保持できるポリモフィックなリストを作成する場合、リストの型で何ができますか?答えはもちろん、何もありません。これらの要素に共通の操作があるという保証はありません。

何1は、一般的に行いますがどちらかである:

  1. 使用GADTsのように、特定の型クラスの要素を含むことができ、リストにする:このアプローチでは

    data FooWrap where 
        FooWrap :: Foo a => a -> FooWrap 
    type FooList = [FooWrap] 
    

    を、あなたドン要素の具体的な型を知っていますが、Foo型の要素を使用してそれらを操作できることがわかります。

    data FooElem = ElemFoo Foo | ElemBar Bar 
    type FooList = [FooElem] 
    

    これは、一定のセットのうちの1つである要素を保持することができるリストを作成する手法1と組み合わせることができる。

  2. リストに含まれる特定の具体的な種類を切り替えるタイプの作成typeclasses。

  3. いくつかの例では、操作関数のリストを構築するために役立つことができます:

    type FooList = [Int -> IO()] 
    

    これは、イベント通知システムのようなもののために有用です。リストに要素を追加するときは、後で行う操作を実行する関数で要素をバインドします。

  4. Data.Dynamic(推奨しません!)を不正行為として使用してください。しかしながら、これは、特定の要素がまったく操作されるという保証を提供しないので、上記のアプローチが好ましいはずである。

+2

+1 "正しい"アプローチを提案する。 –

+0

徹底的な答えをいただき、ありがとうございます。これは私が探しているものです。 – edwardw

+16

N.B. - 'Any'型は、OOP継承階層の最上位にある' Object'クラスであることが一般的な例です* subtyping *を持つ言語では非常に便利です。ハスケルはサブタイピングの本当の概念を持っていないので、コンセプトはここではほとんど役に立たない。 –

12

何も(まあ、何もTypeable)を保持することができますData.DynamicからタイプDynamicがあります。しかし、それは決して正しい方法ではありません。あなたが解決しようとしている問題は何ですか?

+0

こんにちは、私は、いくつかのコードを翻訳するためにErlangのany()型に相当するものを探しています。もう一つは、何かを保持できるポリモーフィックなリストを宣言する方法を知っていることです。それで '[Data.Dynamic.Dynamic]'は? – edwardw

+0

代わりに、 'Dynamic'を使うことをお勧めします。代わりに要素に必要なプロパティを持つ型クラスを作成し、実在する型を使って要素の実際の型を隠します。あなたが望むなら、私は精緻化することができます。 – augustss

11

これはかなり基本的な質問のように聞こえるので、私は誰よりもさらに基本的な答えを出すつもりです。あなたがIntをラップ属性をしたい場合、その属性はタイプAttribute Int、またはタイプAttribute Boolなどを持っているでしょうBoolをラップ属性を持っているでしょう、そして、

data Attribute a = Attribute { name :: String, value :: a } 

:ここでは、ほとんどの場合、適切なソリューションである何これらの属性は任意のタイプの値で作成できます。たとえば、

testAttr = Attribute { name = "this is only a test", value = Node 3 [] } 

と入力して、Attribute (Tree Int)の値を作成することができます。

20

bdonlanの答えに追加:代わりGADTsの、あなたもexistential typesを使用することができます。

{-# LANGUAGE ExistentialQuantification #-} 

class Foo a where 
    foo :: a -> a 

data AnyFoo = forall a. Foo a => AnyFoo a 

instance Foo AnyFoo where 
    foo (AnyFoo a) = AnyFoo $ foo a 

mapFoo :: [AnyFoo] -> [AnyFoo] 
mapFoo = map foo 

これは基本的にbdonlanのGADTソリューションと同等ですが、上のデータ構造の選択を課していない - ことができます例えば、リストの代わりにMapを使用:

import qualified Data.Map as M 

mFoo :: M.Map String AnyFoo 
mFoo = M.fromList [("a", AnyFoo SomeFoo), ("b", AnyFoo SomeBar)] 

data AnyFoo = forall a. Foo a => AnyFoo aビットとしてもGADT表記法で書くことができる:

data AnyFoo where 
    AnyFoo :: Foo a => a -> AnyFoo 
+1

はい、これが方法です。 – augustss

2

データが最終的に特定のタイプである必要がある場合は、GADTでコンバーチブルを使用できます。コンシューマとして、消費する必要があるデータ型にのみ関心があるためです。

{-# LANGUAGE GADTs #-} 
import Data.Convertible 

data Conv b where 
    Conv :: a -> (a -> b) -> Conv b 
    Chain :: Conv b -> (b -> c) -> Conv c 

unconv :: (Conv b) -> b 
unconv (Conv a f) = f a 
unconv (Chain c f) = f $ unconv c 

conv :: Convertible a b => a -> Conv b 
conv a = (Conv a convert) 

totype :: Convertible b c => Conv b -> Conv c 
totype a = Chain a convert 

functor、comonad、およびmonadのインスタンスを取得することはそれほど難しくありません。あなたが興味があれば投稿することができます。

関連する問題