Proxy型
stackage
Data.Proxy
#WIP
https://stackoverflow.com/a/27047260 の前半の説明などを読んで
前提として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を使えばProxyは不要になる
し、もっと簡潔に書ける
TypeApplicationsを使って簡潔に型アノテーションを書く
code:hs
{-# LANGUAGE TypeApplications #-}
main = do
print $ map (read @Int) "33", "4"
print $ show @Int . read $ "42"
#??
何が嬉しい?
Proxyの定義
無いとどうなる
同じ引数の者同士は同じもの?
Proxyという名前
tsで言うとどういうものになるのか
1つもGHC拡張使わずに嬉しい例を見てみたい
TypeApplicationsがあれば不要だったりする?
https://twitter.com/kgtkr/status/1295760737315328000
存在型のこと?
https://twitter.com/kazu_yamamoto/status/784190743116066818
カッコつけて
わかりやさすたのため、こうする
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と同じようなもの
https://riptutorial.com/haskell/example/25926/proxy-is-like---
/haskell-shoen/Proxy
終対象
Voidに型パラメータを与えたようなもの
データは何も持たない
AllowAmbiguousTypesを使えばProxyは不要?
https://www.haskellforall.com/2021/04/how-to-replace-proxy-with.html
https://qiita.com/eielh/items/275a4cc3de033904897a
https://stackoverflow.com/questions/27044209/haskell-why-use-proxy
幽霊型を使ってType-Level Literalsをやる
tsのliteral typeみたいなやつmrsekut.icon
Tagged型
Data.Tagged
https://hackage.haskell.org/package/tagged-0.7.3/docs/Data-Tagged.html
https://qiita.com/kimagure/items/6e383ea0c6e29bf210e5
/haskell-shoen/Proxy
https://kseo.github.io/posts/2017-01-15-data-proxy.html
https://riptutorial.com/haskell/topic/8025/proxies
https://zenn.dev/mod_poppo/books/haskell-type-level-programming/viewer/phantom-types-and-proxy