Proxy型
前提としてread, showの型はこう
code:hs
read :: Read a => String -> a
show :: Show a => a -> String
これを組み合わせたような関数を定義すると、
code:hs
f = show . read
これは普通に考えれば型は、String -> Stringだが、そうするとRead aやShow aの制約が消えてしまうので型安全でない
そのためf :: String -> Stringのような型注釈はエラーになる
かと言って、こういうふうにも定義できない
code:hs
f :: (Read a, Show a) => String -> String
f = show . read
これは、aに制約を入れているが、結局、引数にも戻り値にもaが現れていないため、エラーになる
使用する側から見てもf "hoge"のようになるので、これだけではどういう中間型aを経過すればよいのかわかるはずもない
そこで、aを使用するために、例えばこういう定義ができる
code:hs
{-# LANGUAGE ScopedTypeVariables #-} g :: forall a. (Read a, Show a) => a -> String -> String
g _ = (show :: a -> String) . read
ただただaを型情報を得るためだけに引数に取っている
要は、こういうノリのことがしたい
code:hs
g Int "3"
でも、Haskellは依存型を扱うわけではないので、型を値として扱えない
こうやって利用できる
code:hs
g 0 "3"
この0は数値なら何でもよく、1でも100でも何でも良い
ただ単にIntですよ、というのが伝われば良い
でも、こう書くと「0ってなに??」となる
型情報を伝えるのが責務なのであれば、よりそれに制限されていたほうが良い
そこで、Proxyを使う
今回は自前で定義する
わかりやすさのため、値コンストラクタの名前をずらしている
code:hs
data Proxy t = ProxyC
関数は高定義する
code:hs
{-# LANGUAGE ScopedTypeVariables #-} f :: forall proxy a. (Read a, Show a) => proxy a -> String -> String
f _ = (show :: a -> String) . read
PureScriptでは
code:purs(hs)
class Read a where
read :: String -> a
f ::
∀ a.
Read a =>
Show a =>
Proxy a ->
String ->
String
f _ = (show :: a -> String) <<< read
こう使う
code:hs
f (ProxyC :: Proxy Int) "3"
最終的にはIntであることが伝わればよいのだが、
値であるProxyCを見ただけでは、型のProxy aのaが確定しないため、::で明示する必要がある
結果的にかなり冗長になっているが、仕方がないmrsekut.icon
これが、Proxyの存在意義の一つ
他の例はまだよく知らんmrsekut.icon
し、もっと簡潔に書ける
TypeApplicationsを使って簡潔に型アノテーションを書く
code:hs
{-# LANGUAGE TypeApplications #-} main = do
print $ show @Int . read $ "42"
何が嬉しい?
Proxyの定義
無いとどうなる
同じ引数の者同士は同じもの?
Proxyという名前
tsで言うとどういうものになるのか
1つもGHC拡張使わずに嬉しい例を見てみたい
存在型のこと?
カッコつけて
わかりやさすたのため、こうする
code:hs
data Proxy t = ProxyC
code: (hs)
ghci> import Data.Proxy
ghci> :t ProxC
ProxyC :: Proxy t
ghci> :t ProxyC :: Proxy Int
ProxyC :: Proxy Int :: Proxy Int
わかると自明だが、
1つ目の方は、「ProxyCの型は?」と聞いて、Proxy tです、と返ってきている
2つ目は、「ProxyC :: Proxy Intの型は?」と聞いて、Proxy Intですと返ってきている
括弧を付けるなら、((ProxyC) :: Proxy Int) :: Proxy Intのような感じ
「1 :: Intの型は?」と聞いて、「Intです(そらそう)」と返ってきているのと同じ
code:hs
data Proxy t = Proxy deriving Bounded
code:purs(hs)
data Proxy :: forall k. k -> Type
data Proxy a = Proxy
任意のkindの型を引数に取る
code:purs(hs)
data SProxy :: Symbol -> Type
data SProxy sym = SProxy
Symbolを引数に取る
Proxyでもあるので、この値は、Proxy型を要請する場でも適用できる
本当は型レベルの計算において、モノとモノを区別するためにタグ付けするために使う感じ
ただ、実際には関数も必要になるので(?)、関数側には値コンストラクタを与えるてきな?
code:purs(hs)
zero :: Proxy Zero
zero = Proxy
Voidと同じようなもの
終対象
Voidに型パラメータを与えたようなもの
データは何も持たない
幽霊型を使ってType-Level Literalsをやる tsのliteral typeみたいなやつmrsekut.icon