2017-09-06 3 views
3

GHCのScopedTypeVariablesは、機能パターンではタイプ変数をバインドできますが、パターンではできません。Haskellのスコープ付きタイプ変数は、パターンバインディングでタイプ変数のバインドを許可しないのはなぜですか?

最小限の例として、私はFooの内部型へのアクセスを得るためにしたい場合は、タイプ

data Foo where Foo :: Typeable a => a -> Foo 

を検討し、以下の機能がコンパイルされません:

fooType :: Foo -> TypeRep 
fooType (Foo x) = 
    let (_ :: a) = x 
    in typeRep (Proxy::Proxy a) 

しかし、このトリックを使用して型変数バインディングを関数呼び出しに移動するには、問題なく動作します。

fooType (Foo x) = 
    let helper (_ :: a) = typeRep (Proxy::Proxy a) 
    in helper x 

letバインディングは実際には偽装の関数バインディングなので、なぜ上記の2つのコードスニペットは同等ではありませんか?

(この例では、他のソリューションはtypeOf xTypeRepを作成することです、またはトップレベルの機能にx :: aとして直接変数をバインドします。これらのオプションのどちらもが、私の実際のコードで利用できる、とdoesnのそれらを使用して」質問に答えてください)

+0

を明示的にすべての任意の型の変数を導入する必要はありません: 'fooType(フー・X)= TYPEREP [X] 'typeRep'の' proxy'が任意のファンクタになりうるという事実を利用して動作します:必ずしも些細な 'Proxy'ではなく、実際に' a'値を含むものでもあります! – leftaroundabout

+1

@leftaround私たちが型変数を取り除くなら、さらに進んで 'fooType(Foo x)= typeOf x'と言うことができます。私は先に進み、実際のユースケースは、型変数にアクセスすることが重要な部分であると仮定しました。 – Carl

+0

@Carlええ、実際の 'x'値を含む具象コンテナでも' proxy x'引数を持つ_any_関数を呼び出すことは一般的には知られていません。 – leftaroundabout

答えて

7

大きなことは、機能がcaseの偽装であり、letの表現ではありません。 caseが一致し、letが異なる意味を持つ。これは、let式でタイプの洗練されたGADTコンストラクタと一致させることができない理由です。

caseと一致すると、続行する前に検査結果が評価されますが、letと一致すると、「結果が要求されたときにこの評価を行う」というヒープにサンクがスローされます。 GHCは、怠惰がそれらとやりとりする可能性があるすべての方法でローカルスコープのタイプ(例ではaなど)を保持する方法を知らないので、試してみません。ローカルスコープのタイプが含まれる場合は、怠惰が問題にならないようにcase式を使用します。あなたのコードについては

ScopedTypeVariablesが実際にあなたにはるかに簡潔オプション提供:

{-# Language ScopedTypeVariables, GADTs #-} 

import Data.Typeable 
import Data.Proxy 

data Foo where 
    Foo :: Typeable a => a -> Foo 

fooType :: Foo -> TypeRep 
fooType (Foo (x :: a)) = typeRep (Proxy :: Proxy a) 
+0

私のコードでは、提供されたソリューションを使用することはできませんが、大文字と小文字の説明は質問に答えています。これは、ヘルパー関数を使用するよりも洗練されたソリューションです。ありがとう! – Theelepel

+0

STVはさらに必要ですか?なんらかの理由で実際に 'typeRep'を使用したい場合、' fooType(Foo x)= typeOf x'または 'fooType(Foo x)= typeRep [x]' –

関連する問題