レビティポリモーフィズムとは


81

質問のタイトルが示すように、私はレビティポリモーフィズムとは何か、そしてその動機は何ですか?このページには詳細が含まれていることは知っていますが、説明のほとんどは頭のてっぺんにあります。:)

一方で、このページが少し親しみやすいですが、私はまだそれの背後にある動機を理解することはできませんよ。

回答:


81

注:この回答は、Levityの議論に関するごく最近の観察に基づいています。Levityポリモーフィズムに関するすべては、現在GHC 8.0リリース候補にのみ実装されているため、変更される可能性があります(たとえば、#11471を参照)。


TL; DR:これは、通常の関数では不可能な、リフトされたタイプとリフトされていないタイプに対して関数を多形にする方法です。たとえばので、次のコードは、通常の多型とのチェックを入力しないInt#ようなものがある#が、中に型変数はid種類持っています*

{-# LANGUAGE MagicHash #-}

import GHC.Prim

example :: Int# -> Int# 
example = id            -- does not work, since id :: a -> a
Couldn't match kind ‘*’ with ‘#’
When matching types
  a0 :: *
  Int# :: #
Expected type: Int# -> Int#
  Actual type: a0 -> a0
In the expression: id

(->)まだいくつかの魔法を使用していることに注意してください。


この質問に答える前に、一歩下がって、最も頻繁に使用される関数の1つに進みましょう($)

($)のタイプは何ですか?さて、ハッキングとレポートによると、それは

($) :: (a -> b) -> a -> b

ただし、それは100%完全ではありません。それは便利な小さな嘘です。問題は、ポリモーフィック型(aおよびのようなb)が種類を持っていること*です。しかし、(ライブラリ)の開発者が使用していた($)種類とタイプのためだけでなく*、ほかの種類のものとするために#、例えば

unwrapInt :: Int -> Int#

一方でInt種類がある*(それは下することができます)、Int#種類がある#(とすべての下にはできません)。それでも、次のコードタイプチェック:

unwrapInt $ 42

それはうまくいかないはずです。の戻り値の型を覚えてい($)ますか?それはポリモーフィックであり、ポリモーフィックタイプには種類*があり#ます。では、なぜそれが機能したのですか?最初はバグで、次にハックでした(ghc-devメーリングリストのRyan Scottによるメールの抜粋):

では、なぜこれが起こっているのでしょうか?

長い答えは、GHC 8.0より前のタイプ署名では($) :: (a -> b) -> a -> bb実際には現物*ではなく、むしろであったということOpenKindです。 OpenKindは、持ち上げられた*(種類#)タイプと持ち上げられていない(種類)タイプの両方がそれに生息できるようにするひどいハックです。そのため、タイプチェックが行わ(unwrapInt $ 42) れます。

では($)、GHC 8.0の新しいタイプは何ですか?それは

($) :: forall (w :: Levity) a (b :: TYPE w). (a -> b) -> a -> b
-- will likely change according to Richard E.

それを理解するために、私たちは見なければなりませんLevity

data Levity = Lifted | Unlifted

これ($)で、次の2つの選択肢しかないため、次のいずれかのタイプがあると考えることができますw

-- pseudo types
($) :: forall a (b :: TYPE   Lifted). (a -> b) -> a -> b
($) :: forall a (b :: TYPE Unlifted). (a -> b) -> a -> b

TYPEは魔法の定数であり、種類*を再定義#します。

type * = TYPE Lifted
type # = TYPE Unlifted

種類の数量化もかなり新しく、Haskellの依存型統合の一部です。

Levityポリモーフィズムという名前は、以前のポリモーフィズムの制限では許可されていなかった、または不可能だった、リフトされたタイプとリフトされていないタイプの両方でポリモーフィック関数を記述できるようになったという事実に由来しています。またOpenKind、同時にハックを取り除きます。これは本当に「ちょうど」、両方の種類を処理します。

ちなみに、あなたはあなたの質問だけではありません。Simon Peyton Jonesでさえ、Levity wikiページが必要であると述べ、Richard E.(これの現在の実装者)は、wikiページが現在のプロセスで更新される必要があると述べました。

参考文献


3
「両方の種類の処理」:)
Sibi 2016

4
ええと。私はこのようなものを隠しておく醜いハックを好むと思います。
jberryman 2016

3
なぜ($) :: forall (w1 w2 :: Levity) (a :: TYPE w1) (b :: TYPE w2). (a -> b) -> a -> bそうでは($)ないのですか、つまり、引数が持ち上げられていない関数で使用できないのはなぜですか?
サボテン

3
「バグ」で議論されている@ Cactus
ゼータ

1
@jberryman:#11549RuntimeRep(replaces Levity)を非表示にします。
ゼータ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.