6

私はHListを使用してレコードを作成しようとしています。

HList-GHCSyntaxで定義されている演算子を使用しています。

これまでのところ非常にうまく機能しており、次のようなものを書くことができます。

myRecord = 
  (param1 .=. "param1value") .*. 
  (param2 .=. "param2value") .*. 
  emptyRecord

これにより、次のことが可能になります。

myRecord .!. param1

および以下:

myRecord .!. param3

期待どおりにコンパイルエラーをスローします。param3コンパイル時のパラメータチェックを取得するので、必要に応じてこれはうまく機能します。

ただし、オプションの場合も扱いたいと思いparam3ます。これどうやってするの?


編集:以下は機能しているようです(Empty空のタイプです):

getOptional r l = (hLeftUnion r ((l .=. Empty) .*. emptyRecord)) .!. l

Emptyしかし、私はコードを呼び出す際にチェックする方法を本当に知りません。

4

1 に答える 1

5

定義の問題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上を調べることで判断できますがlr存在しない場合lrどこから来る必要があり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 述語を使用して、これの 2 番目の実装を以下に含めます。これにより、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

編集:これは、すべての Nothing の回答に型注釈が必要な Maybe バージョンです:

{-# 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
             )
于 2012-08-22T12:43:37.887 に答える