型レベル数値リテラル

 型レベル数値リテラル

1a679952cdf455ecd6a15cbde7ae80d5?s=128

Tomohiko Himura

May 09, 2016
Tweet

Transcript

  1. ܕϨϕϧ਺஋Ϧςϥϧ 2016-05-10

  2. ࠷ۙ༡Μͩ͜ͱΛ࿩͢

  3. GHC拡張で遊んでます

  4. https://downloads.haskell.org/%7Eghc/latest/docs/html/users_guide/ghc-language-features.html

  5. GHCで使える Haskellの拡張機能

  6. 型レベルリテラル

  7. 数値で遊んでみました

  8. 型を書くところに 1 2 とか、書けます

  9. ܕΛ֬ೝ͢Δ

  10. HaskelのREPL ghciで :type を使うと値の型が確認できる

  11. ghci> :type 1 1 :: Num a => a 1の型はNumとして扱える型

    ならなんでもよい
  12. ghci> :info Num class Num a where (+) :: a

    -> a -> a (-) :: a -> a -> a (*) :: a -> a -> a negate :: a -> a abs :: a -> a signum :: a -> a fromInteger :: Integer -> a -- Defined in ‘GHC.Num’ instance Num Word -- Defined in ‘GHC.Num’ instance Num Integer -- Defined in ‘GHC.Num’ instance Num Int -- Defined in ‘GHC.Num’ instance Num Float -- Defined in ‘GHC.Float’ instance Num Double -- Defined in ‘GHC.Float’
  13. ghci> :type 1 :: Int 1 :: Int :: Int

    「1 :: Int」の型は Intです
  14. ghci> :type 1 :: Word 1 :: Word :: Word

    ghci> :type 1 :: Float 1 :: Float :: Float
  15. 1というリテラルは いろんな型になれる

  16. undefinedͱ͍͏஋

  17. いろんな型の値になれる 便利な値 undefined

  18. ghci> :type undefined undefined :: t ghci> :type undefined ::

    Int undefined :: Int :: Int ghci> :type undefined :: String undefined :: String :: String
  19. 型レベルリテラルなので 型を書いてるところに 1 とかけるはず

  20. ghci> :type undefined :: 1 <interactive>:1:14: Illegal type: ‘1’ Perhaps

    you intended to use DataKinds
  21. > エラー!! <

  22. そこで1は使えへんよ たぶん DataKindsを使うんよ

  23. DataKinds֦ு

  24. DataKinds カインドを扱えるようになる GHC拡張

  25. ghciでGHC拡張を使うには :set -X拡張名

  26. ghci> :set -XDataKinds ghci> :type undefined :: 1 <interactive>:1:14: Expected

    a type, but ‘1’ has kind ‘GHC.TypeLits.Nat’ In an expression type signature: 1 In the expression: undefined :: 1
  27. > やっぱりエラー!! <

  28. 型がくるところに1があった。 1のカインドがNatだからここでは使えないよ。

  29. ΧΠϯυ

  30. 種(カインド)は「型に対する型」で 「値に型がある」ように 「型には種がある」

  31. :kind を使うと 種を確認できる

  32. ghci> :kind Int Int :: * ghci> :kind Int ->

    Int Int -> Int :: *
  33. ghci> let add = \x y -> x + y

    :: Int ghci> :type add add :: Int -> Int -> Int ghci> :kind Int -> Int -> Int Int -> Int -> Int :: *
  34. 普段つかってる型の種 だいたい *

  35. 種 型 値 1 Int * 関数 Int -> Int

    *
  36. * にならない奴 引数が必要な型

  37. ghci> :kind [] [] :: * -> * ghci> :kind

    Maybe Maybe :: * -> * ghci> :kind Num Num :: * -> GHC.Prim.Constraint ghci> :kind Monad Monad :: (* -> *) -> GHC.Prim.Constraint
  38. それはおいといて :kind には型がかける

  39. ghci> :kind 1 1 :: GHC.TypeLits.Nat

  40. [やったぜ] 型レベル数値リテラル [使えた]

  41. 型レベルの1の種が GHC.TypeLits.Nat のためコンパイルエラー

  42. 種 型 値 1 Int * 1 GHC.TypeLits.Nat ない…

  43. Natをうけとって * になる型とか あればいいのに GHC.TypeLits.Nat -> *

  44. Data.Proxy

  45. ghci> import Data.Proxy ghci> :kind Proxy Proxy :: k ->

    * ghci> :kind Proxy 1 Proxy 1 :: *
  46. ghci> :type undefined :: Proxy 1 undefined :: Proxy 1

    :: Proxy 1
  47. ghci> :type Proxy :: Proxy 1 Proxy :: Proxy 1

    :: Proxy 1 undefinedをつかってきましたが 「Proxy n」な型の値はProxyで作れる
  48. 種 型 値 1 Int * Proxy 1 * Proxy

  49. ܕϨϕϧ਺஋Ϧςϥϧͷԋࢉ

  50. 数値は + とか * で演算できる 型レベルでも当然できるはず

  51. ghci> :kind 1 + 1 <interactive>:1:3: Illegal operator ‘+’ in

    type ‘1 + 1’ Use TypeOperators to allow operators in types
  52. > エラー !! <

  53. [型の演算したいなら] TypeOperators [使え]

  54. ghci> :set -XTypeOperators ghci> :kind 1 + 1 <interactive>:1:3: Not

    in scope: type constructor or class ‘+’
  55. >> エラー !! <<

  56. [存在しない] + [演算子]

  57. [正式名]GHC.TypeLits

  58. ghci> :kind 1 GHC.TypeLits.+ 1 1 GHC.TypeLits.+ 1 :: GHC.TypeLits.Nat

    ghci> import GHC.TypeLits ghci> :kind 1 + 1 1 + 1 :: Nat
  59. :kindの後には型の式が書ける 型の式を評価したいなら:kind!

  60. ghci> :kind! 1 + 1 1 + 1 :: Nat

    = 2
  61. ghci> :kind! 2 * 3 2 * 3 :: Nat

    = 6 ghci> :kind! 6 - 2 6 - 2 :: Nat = 4 ghci> :kind! 2 ^ 3 2 ^ 3 :: Nat = 8 他にもいろいろできます
  62. ghci> :kind! 2 <=? 3 2 <=? 3 :: Bool

    = 'True ghci> :kind! 4 <=? 3 4 <=? 3 :: Bool = 'False ghci> :kind! 2 <= 3 2 <= 3 :: GHC.Prim.Constraint = 'True ~ 'True ghci> :kind! 4 <= 3 4 <= 3 :: GHC.Prim.Constraint = 'False ~ 'True 比較もできます
  63. natValを使えば 型から値をとり出せます

  64. ghci> :type Proxy :: Proxy (1 + 1) Proxy ::

    Proxy (1 + 1) :: Proxy 2 ghci> natVal (Proxy :: Proxy (1 + 1)) 2
  65. ༡ΜͰΈΔ

  66. {-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeOperators #-} import Data.Proxy

    import GHC.TypeLits hoge1 :: Proxy 1 hoge1 = Proxy hoge2 :: Proxy 2 hoge2 = Proxy hoge3 :: Proxy 3 hoge3 = Proxy hoge4 :: Proxy 4 hoge4 = Proxy add' :: Proxy x -> Proxy y -> Proxy (x + y) add' _ _ = Proxy mul' :: Proxy x -> Proxy y -> Proxy (x * y) mul' _ _ = Proxy div' :: Proxy (x * y) -> Proxy x -> Proxy y div' _ _ = Proxy
  67. ghci> hoge1 Proxy ghci> :type hoge1 hoge1 :: Proxy 1

    ghci> hoge2 Proxy ghci> :type hoge2 hoge2 :: Proxy 2 ghci> add' hoge1 hoge2 Proxy ghci> :type add' hoge1 hoge2 add' hoge1 hoge2 :: Proxy 3 ghci> :type mul' hoge2 hoge4 mul' hoge2 hoge4 :: Proxy 8 ghci> :type div' hoge4 hoge2 div' hoge4 hoge2 :: Proxy 2
  68. 実用的な例を 準備できませんでした

  69. ·ͱΊ

  70. • 「値に型がある」ように「型には種がある」 • Haskellには型レベルのリテラルがある • 型レベルの演算子もある • :kind! で型レベルの式を評価できる •

    kindが*にならないと式としてつかえない • (要出典) • 手軽に*にする方法としてProxyがある • 自分でもつくれます(未解説) • :set -X をつかえばghciでもGHC拡張が使える
  71. Haskellならシンプルに表現できる ものがある 自分が得意なプログラミング言語で 同じことを実現してみるとプログラ ミングに対する理解が深まるような 気がします