いくつかのテストコードでは、単体実装を使用して多態関数を実装できる関数を書いています。それはこのようになります使用Data.Type.Equalityパターンマッチングを別の関数に抽出することは可能ですか?
assertEq :: forall a b. (Show a, Typeable a, Typeable b) => a -> (a :~: b)
assertEq x = fromMaybe (error errorMessage) eqT
where errorMessage =
"expected value of type ‘" <> show (typeRep (Proxy :: Proxy b))
<> "’, but got ‘" <> show x
<> "’, which is of type ‘" <> show (typeRep (Proxy :: Proxy a)) <> "’"
:
insertUser :: forall record m. (RelationalEntity record, Monad m) => record -> m (Either (Entity record) (Key record))
insertUser user = case (assertEq user :: record :~: User) of
Refl -> return . Right $ UserId 1234
重要なのは、型シグネチャは、任意のRelationalEntity
のために動作しますが、それは実際にUser
を期待し、私はそれがこのようData.Type.Equality
を使用して実装しました。通常、この種の関数はうまくいくでしょうが、例外が発生すると単にテストが失敗するため、テストコードでうまく動作します。これはまさに私が望むものです。
もちろん、assertEq
を使用しているのはやや言い方です。幸運なことに、Haskellは参照透過性を持っているので、case
の一致をヘルパー関数にパックするのは本当に簡単でしょうか?
withAssertEq :: forall a b c. (Show a, Typeable a, Typeable b) => a -> (b -> c) -> c
withAssertEq x f = case (assertEq x :: a :~: b) of Refl -> f x
は、今私は
insertUser
内から
withAssertEq
を使用することができるはずです::まあ、私はそれを実行しようとしました
insertUser :: forall record m. (RelationalEntity record, Monad m) => record -> m (Either (Entity record) (Key record))
insertUser x = withAssertEq x $ \(_ :: User) ->
return . Right $ UserId 1234
は残念ながら、これはです。TypeCheckしません。タイプチェッカーがRefl
のパターンマッチから得た情報がwithAssertEq
の範囲内で使用に伝播しないので、タイプチェッカーは期待される結果であるKey record
を実際の結果であるKey User
と統一することができません。
はのような種類の情報を伝播する関数を書く方法はありますか?あるいは、タイプ平等に関する情報を型チェッカーに伝えるために、case
式を直接使用するには、にが必要ですか?
本当にタイプ同値の証明がどこにあるのかというと、あなたは 'Refl'を残しておく必要があると思います... – Alec
' assertEq'は私には無意味です: 'a'と' b 'が静的に異なる場合は、型の不一致エラーを生成する代わりに、実行時に' error'を呼び出す同等性のための偽の証人を生成します。これはコンパイル時のエラーを実行時のエラーに効果的に変換するようですが、これは型の世界での犯罪です;-)だから、なぜそれを使いたいのですか?私はその点を完全に逃したと思っています。 – chi
@chi私は100%ということに同意します。それが私がこの疑問に忌み嫌われた理由です。 ;)私は通常、あらゆる種類の部分的な機能を嫌っていますが、これは私のテストスイートで使用されるコードであり、実際のアプリケーションではありません。私は質問にもっと文脈を与えようとしましたが、それはあまりにも混乱していましたので、私はそれを残すことにしました。私は主に、これがXYの問題ではないことを私に信じてくれるように依頼しなければなりません。 :) –