PureScriptの得新型実態
はじめに
PureScript Advent Calendar 2017 - Qiitaの8日目の記事です。 今日は「得新型実態」について書きます。
「得新型実態」ってなによ
derive newtype instance
のことを指しています。面白いと思って全部漢字にしてみたんですけど、次の日に見たら恥ずかくなって消すかもしれません。
そもそもnewtypeってなんやねん
PureScriptで代数的データ型を定義する時は、例えば、以下のように定義すると思います。
data HumanValue = Money Int | Personality | Face | Body
newtype
は上記とは別の意味をもった型宣言のやりかたです。ある型を別の型に見せかけたい、というような時に使います。
newtype Money = Money Int
これで、Money
型とInt
型は、コンパイル時に別の型として扱われます。
さて、このnewtype
宣言には「値コンストラクタは1つで、それが持てる値も1つだけ」という制約があります。
ハンターハンターで覚えたんですが、制約があるってことはその代わりに良いことがあるみたいです。(全然関係ないけど、自分に特殊能力があることを期待して、水見式やったことある人、いると思います。)
以下のコードを見てください。
newtype FirstName = FirstName String data LastName = LastName String foo :: FirstName foo = FirstName "foo" bar :: LastName bar = LastName "bar"
これをコンパイルするとこのようになります。
var LastName = (function () { function LastName(value0) { this.value0 = value0; }; LastName.create = function (value0) { return new LastName(value0); }; return LastName; })(); var FirstName = function (x) { return x; }; var foo = "foo"; var bar = new LastName("bar");
data
で宣言したものはゴニョゴニョと処理があるのに対して、newtype
で宣言した型は、ランタイム上ではnewtype
の中身の型として扱われています。
こう見ると、余計なことしない分、newtype
のほうがパフォーマンスはよさそうですね。これは制約によりもたらされた良いことの1つと言えるでしょう(実感として、この差にどのくらいありがたみを感じるかはアプリケーションによりますが)。
しかしながら、この「ランタイムとしては同じ型として扱われること」によって、もう1つ良いことが発生します。
ということで、次のセクションへ進みます。
derive newtype instance
さて、このnewtype
ですが、こいつをなんらかの型クラスのインスタンスにしたい時ってありますよね?
例として、上述したFirstName
型をEq
のインスタンスにしたいお気持ちが湧いてきたとしましょう。
instance eqFirstName :: Eq FirstName where eq (FirstName s1) (FirstName s2) = eq s1 s2
こんな感じでインスタンスにできますね。
ただ、ちょっと待って欲しい。FirstName
は単にString
をくるんだだけで、かつ、String
はEq
のインスタンスです。
「なんで俺が温もりのある手作業でeq
を定義しなければいけないんだ!なんかもっといい感じにできそうな雰囲気あるだろうが!」
このように思う人はいるはずです。
そんなときにはderive newtype instance
の出番です。
derive newtype instance eqFirstName :: Eq FirstName
あーら不思議、eq
の定義をせずに済みました。これはランタイム上ではnewtype
の値コンストラクタの中身の型と同じ扱いであることによって実現できています。
さらには、こんな型があるとします。
newtype App eff a = App (StateT Int (ExceptT String (Eff eff)) a)
上のEq
の例だけ見ると手作業が許せるかもしれませんが、この例を見ると、さすがにこの型を手作業で各種型クラスのインスタンスにするのはうんざりしそうですね。
derive newtype instance functorApp :: Functor (App eff) derive newtype instance applyApp :: Apply (App eff) derive newtype instance applicativeApp :: Applicative (App eff) derive newtype instance bindApp :: Bind (App eff) derive newtype instance monadApp :: Monad (App eff)
これで万事OKです。人間の幸福度が少し上がりましたね。
これが制約によりもたらされたもう一つの良いことです。
最後に
みなさんは6系統の念能力のどれに憧れましたか?
私は具現化系でした。