純粋な関数型言語では、値を使ってできることは、それに関数を適用することだけです。
言い換えれば、タイプa
の値で何か面白いことをしたい場合は、タイプf :: a -> b
そしてそれを適用します。誰かがあなたに手渡すなら(flip apply) a
タイプ(a -> b) -> b
、それはa
の適切な代替品ですか?
そして、タイプ(a -> b) -> b
? a
の代役のように見えるので、プロキシ、または http://www.thesaurus.com/browse/proxy 。
この質問は、多くのより深い概念への窓です。
まず、この質問にはあいまいさがあります。型forall b. (a -> b) -> b
を意味するので、好きな型でb
をインスタンス化できますか、それとも特定のb
に対して(a -> b) -> b
を意味しますか選択できません。
Haskellでこの区別を形式化できます。
newtype Cont b a = Cont ((a -> b) -> b)
newtype Cod a = Cod (forall b. (a -> b) -> b)
ここにいくつかの語彙があります。最初のタイプは Cont
モナド、2番目は Codensity
Identity
、後者の用語に対する私の精通度は、英語で何と呼ぶべきかを言うほど十分ではありません。
Cont b a
は、a -> b
が少なくともa
と同じ情報を保持できない限り、a
と同等にできません(以下のDan Robertsonのコメントを参照)。したがって、たとえば、Cont
Void
a
から何も取得できないことに注意してください。
Cod a
はa
と同等です。これを確認するには、同型を目撃するだけで十分です。
toCod :: a -> Cod a
fromCod :: Cod a -> a
その実装は演習として残します。本当にそれをやりたいのであれば、このペアが本当に同型であることを証明しようとすることができます。 fromCod . toCod = id
は簡単ですが、toCod . fromCod = id
はCod
に対して 自由定理 を必要とします。
他の答えは、タイプforall b . (a -> b) -> b
とa
の関係を説明する素晴らしい仕事をしましたが、私が取り組んでいるいくつかの興味深い未解決の質問につながるので、1つの警告を指摘したいと思います。
技術的には、forall b . (a -> b) -> b
とa
はnot Haskellのような言語で同型であり、(1)終了しない式を記述でき、(2)は値渡し(厳密)またはseq
を含む。ここでの私のポイントは、not-ではなく、(よく知られているように)Haskellではパラメータが弱くなっていることを示すことですが、それを強化するためのきちんとした方法があり、何らかの意味で再利用される可能性があることですこのような同型。
a
として表現できないforall b . (a -> b) -> b
タイプの用語がいくつかあります。理由を確認するために、練習として残された証明である_box . unbox = id
_を見てみましょう。この証明は実際には彼の答えにあるものよりも興味深いものであることがわかりました。
_box . unbox
= {- definition of (.) -}
\m -> box (unbox m)
= {- definition of box -}
\m f -> f (unbox m)
= {- definition of unbox -}
\m f -> f (m id)
= {- free theorem: f (m id) = m f -}
\m f -> m f
= {- eta: (\f -> m f) = m -}
\m -> m
= {- definition of id, backwards -}
id
_
パラメトリック性が作用する興味深いステップは、自由定理f (m id) = m f
を適用することです。このプロパティは、m
のタイプであるforall b . (a -> b) -> b
の結果です。 m
をa
型の基本値を持つボックスと考える場合、m
が引数でできることは、この基本値に適用して結果を返すことだけです。左側では、これはf (m id)
がボックスから基になる値を抽出し、それをf
に渡すことを意味します。右側では、これはm
がf
を基になる値に直接適用することを意味します。
残念ながら、以下のm
やf
のような用語がある場合、この推論は当てはまりません。
_m :: (Bool -> b) -> b
m k = seq (k true) (k false)
f :: Bool -> Int
f x = if x then ⊥ else 2`
_
f (m id) = m f
を表示したかったことを思い出してください
_f (m id)
= {- definition f -}
if (m id) then ⊥ else 2
= {- definition of m -}
if (seq (id true) (id false)) then ⊥ else 2
= {- definition of id -}
if (seq true (id false)) then ⊥ else 2
= {- definition of seq -}
if (id false) then ⊥ else 2
= {- definition of id -}
if false then ⊥ else 2
= {- definition of if -}
2
m f
= {- definition of m -}
seq (f true) (f false)
= {- definition of f -}
seq (if true then ⊥ else 2) (f false)
= {- definition of if -}
seq ⊥ (f false)
= {- definition of seq -}
⊥
_
明らかに_2
_は_⊥
_と等しくないため、自由定理とa
と_(a -> b) -> b
_の間の同型を失いました。しかし、正確に何が起こったのでしょうか?基本的に、m
は適切に動作するボックスではありません。2つの異なる基になる値に引数を適用するため(そしてこれらのアプリケーションの両方が実際に評価されるようにseq
を使用します)これらの基礎となる値の一方で終了し、他方では終了しない継続で。言い換えると、_m id = false
_はm
をBool
として忠実に表現したものではありません。なぜなら、m
がbothtrue
およびfalse
。
この問題は、次の3つのことの相互作用の結果です。
forall b . (a -> b) -> b
の用語が入力を複数回適用する可能性があるという事実。ポイント1または2を回避することはあまり期待できません。 線形型 は、3番目の問題に対処する機会を与える可能性があります。タイプ_a ⊸ b
_の線形関数は、タイプa
からタイプb
への関数であり、入力を1回だけ使用する必要があります。 m
に型forall b . (a -> b) ⊸ b
が必要な場合、これは自由定理の反例を除外し、a
とforall b . (a -> b) ⊸ b
の間の同型を示すことができます- 非終了およびseq
が存在する場合でも。
これは本当にクールです!直線性は、真の等式推論を困難にする可能性のある効果を調整することにより、興味深い特性を「救う」能力を持っていることを示しています。
ただし、1つの大きな問題が残っています。型forall b . (a -> b) ⊸ b
に必要な自由定理を証明する技術はまだありません。現在の論理関係(通常、このような証明を行うために使用するツール)は、必要な方法で線形性を考慮するようには設計されていません。この問題は、CPS変換を行うコンパイラの正確性を確立することに影響を及ぼします。