OverloadedLabels
GHC-8.0.1
fieldに#を付けた、#fooのような関数を、多相getterとして扱える
OverloadedRecordFieldsの3分割されたものの1つ
docs
wiki
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として利用できる
これは(Magic Type Classesと)HasField型クラスの能力である
ただ、この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
IsLabel型クラスのinstanceを定義する
このinstance定義をするために↑のような3つの拡張が必要になる
ここにもOverloadedLabelsは含まれていないmrsekut.icon
なんでこの前準備を自分で書かないといけないの #??
OverloadedLabelsを有効にしたら暗黙的にやってくれるのでも良い気もするけど
自分で定義するRecord型に依存していないので内部で定義できるはずmrsekut.icon
準備は整ったので、OverloadedLabelsを使うと#hogeでアクセスできるようになる
code:hs
{-# LANGUAGE OverloadedLabels #-}
data Hoge = Hoge { hoge :: Int }
g :: Int
g = #hoge (Hoge 1)
古い情報
OverloadedLabelsが出た当時は、Magic Type Classesがなかったので、
自前でIsLabelのisntanceを定義する必要があった
上の例に合わせるなら
code:hs
instance IsLabel "hoge" (Hoge -> Int) where
fromLabel Hoge { hoge } = hoge
こういうのを自分で定義したRecordの数だけ定義する必要があったmrsekut.icon
GHC 8.2以降はMagic Type Classesがあるのでわざわざ書かなくて良い
#??
というかGHC9.2.1で入った他の拡張を使うのであればOverloadedLabelsの出番はなくなるのではないか?
どういう仕組で動いているのかあまり理解していない
IsLabel型クラスの定義の中身がよくわからない
IsLabelinstanceはxという多相な型に対して定義しているがこれはなんの機能なのか
#hogeという関数の型は、$dIsLabel :: IsLabel "hoge" (Hoge -> Int)と表示されるが、どういう意味なのか
#hogeはなにかの関数のaliasだったりするのか
code
OverloadedLabels#61704a0d19827000003ce870の話を全部一気に書くと
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
g = #hoge (Hoge 1)