OverloadedLabels
fieldに#を付けた、#fooのような関数を、多相getterとして扱える
GHC 9時代に、ボトムアップに理解しようとするとこうなる
だから出た当時の資料とは説明の仕方がやや異なるmrsekut.icon
まず、この拡張を有効にしなくとも以下のようなことができる
code:hs
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DataKinds #-} data Hoge = Hoge { id :: Int }
data Piyo = Piyo { id :: Int }
g :: Int
g = getField @"id" (Hoge 1)
f :: Int
f = getField @"id" (Piyo 1)
2つのRecordがあり、field名が重複しているが、getField @"id"という関数が多相であるので問題なくgetterとして利用できる
ただ、このgetField @"hoge"というgetter部分が冗長
これを#hogeと書けるようにしたい
前準備として以下のようなinstanceを定義しておく
code:hs
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} import GHC.OverloadedLabels (IsLabel (fromLabel))
import GHC.Records (HasField (getField))
instance HasField x r a => IsLabel x (r -> a) where
fromLabel = getField @x
このinstance定義をするために↑のような3つの拡張が必要になる
ここにもOverloadedLabelsは含まれていないmrsekut.icon
なんでこの前準備を自分で書かないといけないの #?? OverloadedLabelsを有効にしたら暗黙的にやってくれるのでも良い気もするけど
自分で定義するRecord型に依存していないので内部で定義できるはずmrsekut.icon
準備は整ったので、OverloadedLabelsを使うと#hogeでアクセスできるようになる
code:hs
{-# LANGUAGE OverloadedLabels #-} data Hoge = Hoge { hoge :: Int }
g :: Int
自前でIsLabelのisntanceを定義する必要があった
上の例に合わせるなら
code:hs
instance IsLabel "hoge" (Hoge -> Int) where
fromLabel Hoge { hoge } = hoge
こういうのを自分で定義したRecordの数だけ定義する必要があったmrsekut.icon
というかGHC9.2.1で入った他の拡張を使うのであればOverloadedLabelsの出番はなくなるのではないか?
どういう仕組で動いているのかあまり理解していない
IsLabelinstanceはxという多相な型に対して定義しているがこれはなんの機能なのか
#hogeという関数の型は、$dIsLabel :: IsLabel "hoge" (Hoge -> Int)と表示されるが、どういう意味なのか
#hogeはなにかの関数のaliasだったりするのか
code
code:hs
{-# LANGUAGE TypeApplications, DataKinds #-} {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables #-} {-# LANGUAGE OverloadedLabels #-} import GHC.OverloadedLabels (IsLabel (fromLabel))
import GHC.Records (HasField (getField))
instance HasField x r a => IsLabel x (r -> a) where
fromLabel = getField @x
data Hoge = Hoge { hoge :: Int }
g :: Int