在他的implicit configuration paper中,Oleg反映了类型级别的值。
使用GHC8的新工具,他的演示可以改进什么? 例如,我们可以删除使用类型应用程序的undefined,但还有什么?
以下是介绍后的原文
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE StandaloneDeriving #-}
#!/usr/bin/env stack
-- stack --resolver lts-9.2 script --package http-conduit --package tagged --package lens
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ExplicitForAll #-}
{-# OPTIONS_GHC -fno-warn-unused-imports -fno-warn-unused-local-binds -fno-warn-missing-signatures #-}
import Prelude
import Control.Lens.Operators ((<&>))
import Data.Function
import Data.Kind
代码
-- Original
--------------
data Zero; data Twice (s :: Type); data Succ (s :: Type); data Pred (s :: Type) -- pure type
class ReflectNum1 s where reflectNum1 :: Num a => s -> a
instance ReflectNum1 Zero where reflectNum1 _ = 0 -- on ne se sert pas de s
instance ReflectNum1 s => ReflectNum1 (Twice s) where reflectNum1 _ = (reflectNum1 (undefined :: s) * 2)
instance ReflectNum1 s => ReflectNum1 (Succ s ) where reflectNum1 _ = (reflectNum1 (undefined :: s) + 1)
instance ReflectNum1 s => ReflectNum1 (Pred s ) where reflectNum1 _ = (reflectNum1 (undefined :: s) - 1)
--
v1 :: Int = reflectNum1 (undefined :: Zero)
使用类型应用程序,我们可以很好地清理未定义的
--- Type Application
-- outillage
newtype Tagged s a = Tagged { unTag :: a } deriving Show
deriving instance Functor (Tagged s)
instance Applicative (Tagged s) where
pure = Tagged
Tagged f <*> Tagged x = Tagged (f x)
_ *> n = n
retag :: Tagged s b -> Tagged t b
retag = Tagged . unTag
class ReflectNum s where reflectNum :: Num a => Tagged s a
instance ReflectNum Zero where reflectNum = Tagged 0
instance ReflectNum s => ReflectNum (Pred s) where reflectNum = (reflectNum @ s) <&> ((-) 1) & retag
instance ReflectNum s => ReflectNum (Succ s) where reflectNum = (reflectNum @ s) <&> ((+) 1) & retag
instance ReflectNum s => ReflectNum (Twice s) where reflectNum = (reflectNum @ s) <&> (* 2) & retag
getReflNum :: forall s a. (ReflectNum s, Num a) => a
getReflNum = unTag (reflectNum @ s)
v3 :: Int = getReflNum @ Zero
我们可以做得更好吗?如表达更多属性,还是以更好的方式表达?