为了学习如何使用haskell中的依赖数据类型,我遇到了以下问题:
假设你有一个如下函数:
mean :: ((1 GHC.TypeLits.<=? n) ~ 'True, GHC.TypeLits.KnownNat n) => R n -> ℝ
在hmatrix
库中定义,那么如何在具有存在类型的向量上使用它? E.g:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
import Data.Proxy (Proxy (..))
import GHC.TypeLits
import Numeric.LinearAlgebra.Static
getUserInput =
let userInput = 3 -- pretend it's unknown at compile time
seed = 42
in existentialCrisis seed userInput
existentialCrisis seed userInput
| userInput <= 0 = 0
| otherwise =
case someNatVal userInput of
Nothing -> undefined -- let's ignore this case for now
Just (SomeNat (proxy :: Proxy n)) ->
let someVector = randomVector seed Gaussian :: R n
in mean someVector -- I know that 'n > 0' but the compiler doesn't
这会出现以下错误:
• Couldn't match type ‘1 <=? n’ with ‘'True’
arising from a use of ‘mean’
确实有道理,但经过一些谷歌搜索和摆弄,我无法找到如何处理这个问题。如何根据用户输入获取n :: Nat
,以使其满足1 <= n
约束?我认为必须有可能,因为someNatVal
函数已经成功地满足KnownNat
约束,基于输入不是负的条件。
在我看来,在使用依赖类型时这是常见的事情,也许答案很明显,但我不会看到它。
所以我的问题:
一般来说,如何在范围内引入存在类型来满足某些函数所需的约束?
我的尝试:
令我惊讶的是,即便进行以下修改
let someVector = randomVector seed Gaussian :: R (n + 1)
给出了类型错误:
• Couldn't match type ‘1 <=? (n + 1)’ with ‘'True’
arising from a use of ‘mean’
此外,向<=?
添加额外的实例以证明此等式不起作用,<=?
已关闭。
我尝试了将GADTs
与类型类组合在一起的方法,如this answer to a previous question of mine中所述,但无法使其正常工作。
答案 0 :(得分:1)
感谢@danidiaz指出我正确的方向,typelist-witnesses
文档为我的问题提供了nearly direct answer。在Google上搜索解决方案时,我似乎使用了错误的搜索字词。
所以这是一个自包含的可编译解决方案:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
import Data.Proxy (Proxy (..))
import Data.Type.Equality ((:~:)(Refl))
import GHC.TypeLits
import GHC.TypeLits.Compare
import Numeric.LinearAlgebra.Static
existentialCrisis :: Int -> Int -> IO (Double)
existentialCrisis seed userInput =
case someNatVal (fromIntegral userInput) of
Nothing -> print "someNatVal failed" >> return 0
Just (SomeNat (proxy :: Proxy n)) ->
case isLE (Proxy :: Proxy 1) proxy of
Nothing -> print "isLE failed" >> return 0
Just Refl ->
let someVector = randomVector seed Gaussian :: R n
in do
print userInput
-- I know that 'n > 0' and so does the compiler
return (mean someVector)
它适用于仅在运行时已知的输入:
λ: :l ExistentialCrisis.hs
λ: existentialCrisis 41 1
(0.2596687587224799 :: R 1)
0.2596687587224799
*Main
λ: existentialCrisis 41 0
"isLE failed"
0.0
*Main
λ: existentialCrisis 41 (-1)
"someNatVal failed"
0.0
typelist-witnesses
看起来很多unsafeCoerce
在幕后。但是界面是类型安全的,因此对于实际使用情况来说并不是那么重要。
修改强>
如果您对此问题感兴趣,也可能会发现这篇文章很有趣:https://stackoverflow.com/a/41615278/2496293