Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/react-native/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 哈斯凯尔Idris的Fin的首选替代方案是什么_Haskell_Dependent Type_Idris - Fatal编程技术网

Haskell 哈斯凯尔Idris的Fin的首选替代方案是什么

Haskell 哈斯凯尔Idris的Fin的首选替代方案是什么,haskell,dependent-type,idris,Haskell,Dependent Type,Idris,我希望有一个可以包含值0到n的类型,其中n位于类型级别 我试着说: import GHC.TypeLits import Data.Proxy newtype FiniteNat n = FiniteNat { toInteger :: Integer } smartConstructFiniteNat :: (KnownNat n) => Proxy n -> Integer -> Maybe (FiniteNat (Proxy n)) smartConstructFin

我希望有一个可以包含值0到n的类型,其中n位于类型级别

我试着说:

import GHC.TypeLits
import Data.Proxy

newtype FiniteNat n = FiniteNat { toInteger :: Integer }

smartConstructFiniteNat :: (KnownNat n) => Proxy n -> Integer -> Maybe (FiniteNat (Proxy n))
smartConstructFiniteNat pn i 
  | 0 <= i && i < n = Just (FiniteNat i)
  | otherwise       = Nothing
  where n = natVal pn
导入GHC.TypeLits
导入数据。代理
新类型FiniteNat n=FiniteNat{toInteger::Integer}
smartConstructFiniteNat::(KnownNat n)=>Proxy n->Integer->Maybe(FiniteNat(Proxy n))
SMARTCONSTRUCTENTENAT pn i

|0您可以直接将Idris的
Fin
转换为通常的Haskell mishmash类型的依赖类型功能

data Fin n where
    FZ :: Fin (S n)
    FS :: Fin n -> Fin (S n)

(!) :: Vec n a -> Fin n -> a
(x :> xs) ! FZ = x
(x :> xs) ! (FS f) = xs ! f
使用
TypeInType
您甚至可以使用singleton
Fin
s

data Finny n (f :: Fin n) where
    FZy :: Finny (S n) FZ
    FSy :: Finny n f -> Finny (S n) (FS f)
这允许您在运行时的东西上伪造依赖量化,例如

但是,呃,必须在值和类型级别复制所有内容有点糟糕,并且写出所有类型的变量(
n
)可能会变得相当乏味


如果你真的确定你需要一个高效的
Fin
运行时表示,你基本上可以做你在问题中所做的事情:把一台机器
Int
塞进一个
newtype
中,并使用幻影类型作为它的大小。但是,作为库实现者,您有责任确保
Int
符合范围

newtype Fin n = Fin Int

-- fake up the constructors
fz :: Fin (S n)
fz = Fin 0
fs :: Fin n -> Fin (S n)
fs (Fin n) = Fin (n+1)
此版本缺少真正的GADT构造函数,因此无法使用模式匹配操作类型相等。您必须自己使用
unsafeccerce
。您可以以
fold
的形式为客户端提供一个类型安全的接口,但他们必须愿意以更高的顺序编写所有代码,而且(因为
fold
是一种亚同构),一次查看多个层变得更加困难

-- the unsafeCoerce calls assert that m ~ S n
fold :: (forall n. r n -> r (S n)) -> (forall n. r (S n)) -> Fin m -> r m
fold k z (Fin 0) = unsafeCoerce z
fold k z (Fin n) = unsafeCoerce $ k $ fold k z (Fin (n-1))
哦,你不能用这个
Fin
表示法进行类型级计算(就像我们在上面对
Fin2Nat
所做的那样),因为类型级
Int
不允许归纳

值得一提的是,Idris的
Fin
与上面的GADT一样低效。这些文件包括:

在算术中使用
Fin
可能不是一个好主意,而且它们在运行时效率极低

我听说Idris的未来版本能够识别“
Nat
with types”样式的数据类型(如
Fin
),并自动擦除校对并将值打包成机器整数,但据我所知,我们还没有做到这一点。

rampion模式同义词,我同意,但不可否认,解决如何正确构建他们的签名并不是一件小事。因此,我想我应该写一个正确的答案来给出完整的代码

首先,通常的样板文件:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE Trustworthy #-}

module FakeFin (Nat (..), Fin (FZ, FS), FinView (..), viewFin) where
import Numeric.Natural
import Unsafe.Coerce
现在,基本类型:

data Nat = Z | S Nat

-- Fin *must* be exported abstractly (or placed in an Unsafe
-- module). Users can use its constructor to implement
-- unsafeCoerce!
newtype Fin (n :: Nat) = Fin Natural
deriving instance Show (Fin n)
通过视图类型工作比直接工作容易得多,因此让我们定义一个:

data FinView n where
  VZ :: FinView ('S n)
  VS :: !(Fin n) -> FinView ('S n)
deriving instance Show (FinView n)
需要注意的是,我们可以使用显式等式约束定义
FinView
,因为我们必须从这些方面考虑,才能给出正确的模式签名:

data FinView n where
  VZ :: n ~ 'S m => FinView n
  VS :: n ~ 'S m => !(Fin m) -> FinView n
现在,实际查看功能:

viewFin :: Fin n -> FinView n
viewFin (Fin 0) = unsafeCoerce VZ
viewFin (Fin n) = unsafeCoerce (VS (Fin (n - 1)))
模式签名精确地反映了
FinView
构造函数的签名

pattern FZ :: () => n ~ 'S m => Fin n
pattern FZ <- (viewFin -> VZ) where
  FZ = Fin 0

pattern FS :: () => n ~ 'S m => Fin m -> Fin n
pattern FS m <- (viewFin -> VS m) where
  FS (Fin m) = Fin (1 + m)

-- Let GHC know that users need only match on `FZ` and `FS`.
-- This pragma only works for GHC 8.2 (and presumably future
-- versions).
{-# COMPLETE FZ, FS #-}

finZBad
是所有操作发生的地方,但它不会做任何不适当的事情!如果有人真的给了我们一个Fin'Z类型的非底部值,那么有些事情已经严重出错了。这里的显式类型相等证据是必要的,因为如果GHC发现代码缺少
'Z~'S m
,它将立即拒绝它;GHC并不真正喜欢约束中的假设推理。
NOINLINE
注释是必要的,因为GHC的简化器本身使用类型信息;处理它非常清楚的事情的证据是不可能的,这会使它非常困惑,产生极其武断的结果。因此,我们阻止了它并成功地实现了邪恶功能。

您可以轻松地翻译Idris的
Fin
数据Fin n,其中{FZ::Fin(sn);FS::Fin n->Fin(sn)}
。如果你确定你需要一个高效的运行时表示,那么你基本上必须做你在问题中所做的事情——将一台机器
Int
塞入
newtype
并使用幻影类型作为它的大小。为了弥补GADT构造函数的不足,您必须使用
fold::(forall n.rn->r(sn))->(forall n.r(sn))->Fin m->rm
(实现
fold
需要
不完美的
)。Orgazoid-您应该升级到一个答案。@rampion好主意:)为了它的价值,你在问题中写的东西已经作为library@JustinL. 谢谢,这可能是目前对我最有用的。有没有实现类似功能的库?您也可以使用
patternymonyms
将上面的构造函数值生成双向模式-
patternFZ::Fin(sn);模式FZ=Fin 0
进行危险优化的安全方法是使用
Natural
而不是
Int
。这样就避免了溢出的麻烦。如果@rampion没有提出模式同义词,我会;这是提供接口的正确方式。一定要使用GHC 8.2的
COMPLETE
pragma,让它知道
FZ
FS
涵盖了所有情况。@rampion,你的要点实际上并不完全正确。我们需要匹配模式以显示类型信息。对于您的代码,我们需要类型信息来使用模式。例如,尝试编写与构造函数匹配的函数
f::Fin n->Int
;它不会进行类型检查,因为它需要知道
n
是使用这些模式的继承者。看看我的答案。@rampion,你能从中得到更多。当您通过
FS
构造函数进行模式匹配时,您将越来越多地了解
Fin
索引。你永远不可能把它完全钉住(没有额外的证据),但你可以从下面把它绑起来。
pattern FZ :: () => n ~ 'S m => Fin n
pattern FZ <- (viewFin -> VZ) where
  FZ = Fin 0

pattern FS :: () => n ~ 'S m => Fin m -> Fin n
pattern FS m <- (viewFin -> VS m) where
  FS (Fin m) = Fin (1 + m)

-- Let GHC know that users need only match on `FZ` and `FS`.
-- This pragma only works for GHC 8.2 (and presumably future
-- versions).
{-# COMPLETE FZ, FS #-}
import Data.Type.Equality

type family YahF n a b where
  YahF 'Z a _ = a
  YahF _ _ b = b

newtype Yah n a b = Yah (YahF n a b)

{-# NOINLINE finZBad #-}
finZBad :: 'Z :~: n -> Fin n -> a -> b
finZBad pf q =
  case q of
    FZ -> blah (trans pf Refl)
    FS _ -> blah (trans pf Refl)
  where
    blah :: forall a b m. 'Z :~: 'S m -> a -> b
    blah pf2 a = getB pf2 (Yah a)

    {-# NOINLINE getB #-}
    getB :: n :~: 'S m -> Yah n a b -> b
    getB Refl (Yah b) = b

myUnsafeCoerce :: a -> b
myUnsafeCoerce = finZBad Refl (Fin 0)