OverloadedLabels
fieldに #
を付けた、 #foo
のような関数を、多相getterとして扱える
GHC 9時代に、ボトムアップに理解しようとするとこうなる
だから出た当時の資料とは説明の仕方がやや異なる

まず、この拡張を有効にしなくとも以下のようなことができる
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を定義しておく
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
は含まれていない

なんでこの前準備を自分で書かないといけないの
#?? OverloadedLabels
を有効にしたら暗黙的にやってくれるのでも良い気もするけど
自分で定義するRecord型に依存していないので内部で定義できるはず

準備は整ったので、 OverloadedLabels
を使うと #hoge
でアクセスできるようになる
hs{-# LANGUAGE OverloadedLabels #-}
data Hoge = Hoge { hoge :: Int }
g :: Int
g = #hoge (Hoge 1)
自前で IsLabel
のisntanceを定義する必要があった
上の例に合わせるなら
hsinstance IsLabel "hoge" (Hoge -> Int) where
fromLabel Hoge { hoge } = hoge
こういうのを自分で定義したRecordの数だけ定義する必要があった

というかGHC9.2.1で入った他の拡張を使うのであれば OverloadedLabels
の出番はなくなるのではないか?
どういう仕組で動いているのかあまり理解していない
IsLabel
instanceは x
という多相な型に対して定義しているがこれはなんの機能なのか
#hoge
という関数の型は、 $dIsLabel :: IsLabel "hoge" (Hoge -> Int)
と表示されるが、どういう意味なのか
#hoge
はなにかの関数のaliasだったりするのか
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
g = #hoge (Hoge 1)