[PureScript] Visible type applicationで不要な型アノテーションとおさらばする
少し前にリリースされた PureScript v0.15.10 からVisible type application
が実装されたようです(Haskellには言語拡張として既に存在する)。
使ってみて、個人的には嬉しいことがあったので、紹介してみようと思います。
Visible type application って何?
Visible type application
とは、多相関数の型変数を特定の型にインスタンス化する方法です。
(この場合のapplicationとはWebアプリとかスマホアプリとかのapplicationではなく、文脈的に「適用」の意味で用いられていると思われます)
上記の説明ではよくわからないと思うので、リリースノートの例を交えながら説明していきます。
リリースノートでは、次のような多相関数id
を例に説明しています。
id :: forall @a. a -> a
id a = a
型変数a
に着目してください。頭に@
がついています。
この@a
はvisible type variable
と呼ばれるもので、このvisible type variable
を特定の型にインスタンス化できるのです。
こちらが使用例です。
idInt :: Int -> Int
idInt = id @Int
example :: Int
example = id @Int 0
@Int
のように型が明示(可視化)されています。
この例の場合、型推論できるのでVisible type application
を使わなくてもコンパイルは通りますね。
これだけだと嬉しさが伝わりづらいと思いますので、後でVisible type application
ができると嬉しい例を紹介します。
ちなみにclass
やdata
は@
を使わずとも自動的にvisible type variable
になっています。
なので既存のライブラリのclass
やdata
がVisible type application
に対応するのを待つ、ということはしなくてよいです。
また、この変数の数は複数個いけます。
example = Left @Int @String 0
最後に、visible type variable
はワイルドカード(_
)でスキップできます。
example = Left @_ @_ 0
e1 :: Either Int String
e1 = example
e2 :: Either Int Int
e2 = example
Visible type application
の嬉しさ
Visible type application
の嬉しさは型アノテーションを置き換えられるところにあります。
どういうことかというと、型推論が効かないケースで今までa :: Foo Int
のように型アノテーションを書いていたところを、a @Int
と書けるわけです。
個人的には簡潔でこちらの方が好きです。
Visible type application
の元ネタの論文でも、「型アノテーションは回りくどい、型を指定できる方がはるかに直接的」というようなことが書かれています。同意です。
嬉しかった具体例
私はpmockというモックライブラリを開発しています。
このライブラリが提供しているモックはverify
系の関数を使うことで、特定の引数で呼び出されたことを検証することができます。
その検証において、任意の値を期待値とするany
という関数を使うことができるですが、これまでは次のように型アノテーションを書く必要がありました。
import Prelude
import Test.PMock (Param, any, mock, verifyCount, (:>))
import Test.Spec (Spec, describe, it)
spec :: Spec Unit
spec = do
describe "Example Spec" do
it "any match example" do
let
m = mock $ "Title" :> 2023 :> false
verifyCount m 0 $ (any :: Param String) :> (any :: Param Int)
これがVisible type application
のおかげで、このように書けるようになったわけです。
verifyCount m 0 $ any @String :> any @Int
シンプル!嬉しい!
その他にも、次のような例があります。
import Prelude
import Effect.Aff (Aff)
import Test.PMock (fun, mock, verify, (:>))
import Test.Spec (Spec, describe, it)
import Test.Spec.Assertions (shouldEqual)
spec :: Spec Unit
spec = do
describe "Example Spec" do
it "Return Monad." do
let
m = mock $ "Article Id" :> (pure { title: "Article Title" } :: Aff { title :: String })
result <- fun m "Article Id"
result `shouldEqual` {title: "Article Title"}
verify m "Article Id"
この例では文字列を受け取ってpure { title :: String }
を返すモック関数を定義していますが、pure
が返す型を推論できないため、型アノテーションをつけています。
Visible type application
を使うとこれを次のように書けます!
mock $ "Article Id" :> pure @Aff { title: "Article Title" }
やったぜ!
いかがでしたか?
型推論が効かない場合や何らかの理由で型を明示したい場合などに、やむなく型アノテーションを書いていたところをVisible type application
を使うことで簡潔に書けるようになったと思います。
(好き嫌いは個人の感覚に依るところが大きいと思いますが)
わかってしまえば使うのは簡単なので、頭の片隅にでも入れておいて損はないと思います。
では。また。
Discussion