Haskell: HList и дополнительные параметры
Я пытался использовать HList для создания записей.
я использую операторы, определенные в HList-GHCSyntax.
он до сих пор работает достаточно хорошо, что позволяет мне писать такие вещи:
myRecord =
(param1 .=. "param1value") .*.
(param2 .=. "param2value") .*.
emptyRecord
это позволяет мне сделать следующее:
myRecord .!. param1
и
myRecord .!. param3
выдает ошибку компиляции, как ожидалось. Это отлично работает, если param3
требуется, так как я получаю время компиляции проверка параметров.
но я также хочу иметь дело со случаем, когда param3
является необязательным. Как я могу это сделать?
Edit: следующий, кажется, работает (Empty
является пустым типом):
getOptional r l = (hLeftUnion r ((l .=. Empty) .*. emptyRecord)) .!. l
но я действительно не знаю, как проверить Empty
в вызывающем коде.
1 ответов
проблема с определением getOptional
определяет тип результата. Если попытаться:
class GetOptional r l v | r l -> v where
getOptional :: l -> Record r -> Maybe vs
или
class GetOptional r l v | r l -> v where
getOptional :: l -> Record r -> Maybe v
затем v
можно определить, посмотрев вверх l
на r
когда присутствует, но если l
не r
тогда откуда следует v
приходят? Выбрать () или пустой? Оставляя функциональную зависимость, пользователь предоставляет аннотацию типа где-то.
возможно, лучший способ-предоставить значение по умолчанию (например fromMaybe):
class GetOptional r l v where
getOptional :: l -> v -> Record r -> v
более сложная версия может предоставить функцию для использования существующего значения (v->w)
значение по умолчанию w
.
это работает для меня:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
import Data.HList.FakePrelude(HEq,HTrue,HFalse)
import Data.HList.HListPrelude(HNil(HNil),HCons(HCons))
import Data.HList.GhcSyntax((.=.),(.*.))
import Data.HList.Record(Record(Record),LVPair(LVPair),emptyRecord)
class GetOptional l r v where
getOptional :: l -> v -> Record r -> v
instance GetOptional l HNil v where
getOptional _ v _ = v
instance ( HEq l l' b
, GetOptional' b l (HCons (LVPair l' v') r) v
)
=> GetOptional l (HCons (LVPair l' v') r) v where
getOptional l v (Record r) = getOptional' (undefined :: b) l v r
class GetOptional' b l r v where
getOptional' :: b -> l -> v -> r -> v
instance GetOptional' HTrue l (HCons (LVPair l v) r) v where
getOptional' _ _ _ (HCons (LVPair v) _) = v
instance ( GetOptional l r v
)
=> GetOptional' HFalse l (HCons (LVPair l' v') r) v where
getOptional' _ l v (HCons _ r) = getOptional l v (Record r)
data L1 = L1
data L2 = L2
e = emptyRecord
f = L1 .=. True .*. emptyRecord
-- test1 :: Bool
test1 = getOptional L1 False f
-- test2 :: Bool
test2 = getOptional L1 False e
-- test3 :: ()
test3 = getOptional L2 () f
-- test4 gives a type error:
-- test4 = getOptional L1 () f
я также включаю ниже вторую реализацию этого, используя предикаты HList "более высокого уровня". Это удаляет класс типа GetOptional и делает getOptional простой функцией:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
import Data.HList.FakePrelude(HFalse,HTrue)
import Data.HList.HListPrelude(HMember,hMember)
import Data.HList.GhcSyntax((.=.),(.*.))
import Data.HList.Record(RecordLabels,Record,HasField(hLookupByLabel),recordLabels,emptyRecord)
-- This type is inferred properly
-- getOptional :: ( RecordLabels r ls
-- , HMember l ls b
-- , GetOptional' b l r v )
-- => l -> v -> Record r -> v
getOptional l v rec = getOptional' (hMember l (recordLabels rec)) l v rec
class GetOptional' b l r v where
getOptional' :: b -> l -> v -> Record r -> v
instance GetOptional' HFalse l rec v where
getOptional' _ _ v _ = v
instance ( HasField l r v )
=> GetOptional' HTrue l r v where
getOptional' _ l _ r = hLookupByLabel l r
data L1 = L1
data L2 = L2
e = emptyRecord
f = L1 .=. True .*. emptyRecord
-- test1 :: Bool
test1 = getOptional L1 False f
-- test2 :: Bool
test2 = getOptional L1 False e
-- test3 :: ()
test3 = getOptional L2 () f
-- test4 gives a type error:
-- test4 = getOptional L1 () f
EDIT: вот возможно версия, которая нуждается в аннотациях типа для всех ничто отвечает:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
import Data.HList.FakePrelude(HFalse,HTrue)
import Data.HList.HListPrelude(HMember,hMember)
import Data.HList.GhcSyntax((.=.),(.*.))
import Data.HList.Record(RecordLabels,Record,HasField(hLookupByLabel),recordLabels,emptyRecord)
import Data.HList.TypeCastGeneric1
import Data.HList.TypeEqGeneric1
import Data.HList.Label5
-- getOptional :: ( RecordLabels r ls
-- , HMember l ls b
-- , GetOptional' b l r v )
-- => l -> Record r -> Maybe v
getOptional l rec = getOptional' (hMember l (recordLabels rec)) l rec
class GetOptional' b l r v where
getOptional' :: b -> l -> Record r -> Maybe v
instance GetOptional' HFalse l rec v where
getOptional' _ _ _ = Nothing
instance ( HasField l r v )
=> GetOptional' HTrue l r v where
getOptional' _ l r = Just (hLookupByLabel l r)
data L1 = L1
data L2 = L2
e = emptyRecord
f = L1 .=. True .*. emptyRecord
test1 = getOptional L1 f
test2 = getOptional L1 e
test3 = getOptional L2 f
-- test4 :: Maybe () -- this would be a type error
-- test4 = getOptional L1 f
main = print ( test1 -- inferred becuase it is Just {}
, test2 :: Maybe () -- must specify for Nothing
, test3 :: Maybe () -- must specify for Nothing
)