あどけない話

Internet technologies

重複したフィールドラベル

Haskell 2010 では、同じファイルに重複したフィールドラベルを定義できない。たとえば、以下はエラーになる。

data Foo = Foo { same :: Int }
data Bar = Bar { same :: Float } -- これはダメ

この問題を解決する案は、OverloadedRecordFields と呼ばれ、苦難の歴史を持つ。実装があるにもかかわらず、

  • 実装が一枚岩
  • コードの複雑になる割に利益が少ない

などの理由により、GHC へはマージされずにいた。現在では、OverloadedRecordFieldsは、三つの拡張へと分割された:

  1. DuplicateRecordFields
  2. OverloadedLabels
  3. Magic type classes

この中、1. と 2. が GHC 8.0 に入る。

DuplicateRecordFields

DuplicateRecordFields は単純で、同一ファイルで重複したフィールドラベルが「定義」されることを許す。以下は、GHC 8.0 ではエラーにならない。

{-# LANGUAGE DuplicateRecordFields #-}

data Foo = Foo { same :: Int }
data Bar = Bar { same :: Float }

しかし、フィールドラベルを使おうとするとエラーになる。

{-# LANGUAGE DuplicateRecordFields #-}

data Foo = Foo { same :: Int }
data Bar = Bar { same :: Float }

main :: IO ()
main = do
    let x = Foo 1
    print $ same x -- これはダメ

型推論はどこで完了するか分からないので、型を取ってこれないらしい。型を明記すれば使える。

{-# LANGUAGE DuplicateRecordFields #-}

data Foo = Foo { same :: Int }
data Bar = Bar { same :: Float }

main :: IO ()
main = do
    let x = Foo 1
    print $ same (x :: Foo)

これはやってられない感じがする。

OverloadedLabels

この問題を解決するための第一歩が OverloadedLabels である。とっても面倒だが、以下のような記述で、# が付いたフィールドラベルが使えるようになる。

{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE DataKinds, TypeFamilies #-}
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}

import GHC.OverloadedLabels

data Foo = Foo { same :: Int }
data Bar = Bar { same :: Float }

instance (a ~ Int) => IsLabel "same" (Foo -> a) where
  fromLabel _ (Foo n) = n

instance (a ~ Float) => IsLabel "same" (Bar -> a) where
  fromLabel _ (Bar d) = d

main :: IO ()
main = do
    let x = Foo 1
    print $ #same x

将来の GHC では、このボイラープレートが自動導出されるようになるそうだ。