2015-10-11 34 views
7

正確な型パラメータが隠されているパラメータ化型を生成するHaskell関数を記述できますか?私。 f :: T -> (exists a. U a)のようなもの?明白な試み:私は気にしないことGHCを説得する方法があるならば、私は、これは不自然な例ですけど、私は好奇心が強いHaskell関数が存在型を返す

Couldn't match type `a1' with `a' 
    `a1' is a rigid type variable bound by 
     a pattern with constructor 
     Wrap :: forall a. D a -> Wrap, 
     in an equation for `unwrap' 
     at test.hs:8:9 
    `a' is a rigid type variable bound by 
     the type signature for unwrap :: Wrap -> D a at test.hs:7:11 
Expected type: D a 
    Actual type: D a1 
In the expression: d 
In an equation for `unwrap': unwrap (Wrap d) = d 

{-# LANGUAGE ExistentialQuantification #-} 

data D a = D a 

data Wrap = forall a. Wrap (D a) 

unwrap :: Wrap -> D a 
unwrap (Wrap d) = d 

を使用してコンパイルに失敗しますDがパラメータ化された正確な型。unwrapの結果の別の実体ラッパー型を導入しません。明確にするために

、私は型の安全性をしたいですが、またunwrapの結果約aを(例えばそれだけでDからStringフィールドを抽出しているため)気にしない機能dToString :: D a -> Stringを適用できるようにしたいと思います。私はそれを達成する他の方法があることを理解しています(例えば、wrapToString (Wrap d) = dToString dを定義しています)。なぜなら、そのような隠蔽が実在しない根本的な理由があるかどうかに、より興味があります。

+3

私は理由が存在すると思います。 D a'はHaskellの一部ではありません。冗長性を避けるための単純な要望です。この型は 'forall rと同じです。 (for a a D a - > r) - > r'であるので、実在の値を表現するのにも同様に使うことができます。 –

+0

@ n.m。厳密に言えばそれは同等ではないが、私はそのような存在価値に行える唯一の有用なことは、関数forall aの適用であることに同意する。 D a - > r'なので、このアドホックな存在感を表現できないのは感謝します。 –

答えて

11

はい、できますが、簡単な方法ではありません。

{-# LANGUAGE ExistentialQuantification #-} 
{-# LANGUAGE RankNTypes #-} 

data D a = D a 

data Wrap = forall a. Wrap (D a) 

unwrap :: Wrap -> forall r. (forall a. D a -> r) -> r 
unwrap (Wrap x) k = k x 

test :: D a -> IO() 
test (D a) = putStrLn "Got a D something" 

main = unwrap (Wrap (D 5)) test 

あなたの関数からD something_unknownを返すことはできませんが、あなたはそれを抽出し、すぐに示すように、D aを受け入れる別の関数に渡すことができます。  

7

はい、Dがパラメータ化されている正確なタイプを気にしないことをGHCに納得させることができます。ただ、それは恐ろしい考えです。

{-# LANGUAGE GADTs #-} 

import Unsafe.Coerce 

data D a = D a deriving (Show) 

data Wrap where  -- this GADT is equivalent to your `ExistentialQuantification` version 
    Wrap :: D a -> Wrap 

unwrap :: Wrap -> D a 
unwrap (Wrap (D a)) = D (unsafeCoerce a) 

main = print (unwrap (Wrap $ D "bla") :: D Integer) 

これは私が簡単なプログラムの実行時に何が起こるかです:

enter image description here

などをメモリ消費は、システムをダウンさせますまで、。

種類が重要です!タイプシステムを迂回する場合、プログラムの予測可能性を回避します(thermonuclear warや有名なdemons flying out of your noseなど)。


ここで明らかに、タイプはどういうわけか違うと思います。 Pythonなどの動的言語やJavaなどのOO言語の程度では、型はある意味では値が持つことができるプロパティです。したがって、(参照)値は、単一のタイプの異なる値を区別するために必要な情報だけでなく、異なる(サブ)タイプを区別するための情報も持ちます。それは多くの意味でむしろ非効率的です。–これは、Pythonが非常に遅く、Javaにそのような巨大なVMが必要な主な理由です。

ハスケルでは、タイプは存在しません実行時。関数は、値がどのタイプのもので動作しているかを決して知らない。 コンパイラが持っているタイプについてすべて知っているので、関数はを必要としません。そのような知識–コンパイラはすでにそれをハードコードしています! (それはあなたが、私はそれが思ったほど安全ではありません示されているようにunsafeCoerce、とそれを回避しない限り、です。)あなたが値に“プロパティ”ようなタイプを添付したいならば、あなたは明示的に行う必要があり

そして、それはそれらの実在のラッパーがそこにあるものです。しかし、通常は関数型言語でそれを行うより良い方法があります。本当にこれが欲しいアプリケーションは何ですか?


おそらく、多形性結果が意味するシグネチャを思い出すことも役立ちます。 unwrap :: Wrap -> D aは“を意味するものではなく、結果はD a ... a ”の方が気にしない方が良いです。これはJavaの場合に当てはまりますが、ハスケルではむしろ役に立たないでしょう。なぜなら、未知の型の値では何もできないからです。

は、代わりにそれは意味:どんなタイプa発信者の要求をするために、この関数は、適切なD a値を供給することができます。もちろんこれは–を提供するのは難しいですが、特別な情報がなくても、与えられた不明なタイプの値で何かをするのと同じように不可能です。 a値は引数、またはaにすでに存在する場合でも、何とか例えばfromInteger :: Num a => Integer -> a、それはかなり可能と非常に便利です(型クラスに制限されます。


はの独立したStringフィールド–を取得するにはWrapより上で、このような関数を作成するには

data D a = D 
    { dLabel :: String 
    , dValue :: a 
    } 

data Wrap where Wrap :: D a -> Wrap 

labelFromWrap :: Wrap -> String 
labelFromWrap (Wrap (D l _)) = l 

:あなただけのラップ値を直接操作することができますaパラメータ–一般的には“ラベルアブソーバー(a ”を気にしない)の場合は、n.m.の答えに示すようにRank2-polymorphismを使用します。

+0

タイプシステムを迂回する必要はありません。もちろん、あなたが 'unsafeCoerce'をしたら何も起こることはありませんが、どうしてですか? –

+1

@ n.m。 OPがそれを求めたからです。 – leftaroundabout

+1

OPが 'unsafeCoerce'についてどこに尋ねるのか分かりません。 –

関連する問題