的类型^
是:
Cryptol> :t (^)
(^) : {a} (Logic a) => a -> a -> a
请注意,它要求两个参数完全相同。您收到类型错误[2]
是因为与[8]
;不同 因为它们的大小不同。与 Verilog 不同,Cryptol 不会隐含地“填充”东西,我认为 Cryptol 在这里绝对做对了。Verilog 程序员可以解决由于隐式转换而导致的无数错误。
Cryptol 中的所有此类转换都必须是明确的。
在 Cryptol 中处理这种情况的典型方法是使用多态常量zero
:
Cryptol> :t zero
zero : {a} (Zero a) => a
该值适用于zero
所有类型(您Zero
现在可以忽略约束),并且您可以想象在这种情况下是“正确的”填充值。因此,您将函数定义为:
Cryptol> let test(x:[2], y:[8]) = (zero#x)^y
Cryptol> :t test
test : ([2], [8]) -> [8]
并像这样使用它:
Cryptol> test (1, 5)
0x04
如果你出于某种原因想在右边填充,你会这样做:
Cryptol> let test(x:[2], y:[8]) = (x#zero)^y
Cryptol> test(1,5)
0x45
这样一来,一切都是明确的,您不必知道所有关于如何填充以成为正确尺寸的神奇规则。
如果你想获得真正的幻想,那么你可以这样做:
Cryptol> let test(x, y) = (zero#x)^(zero#y)
Cryptol> :t test
test : {n, m, i, j, a} (Logic a, Zero a, m + i == n + j, fin n,
fin m) =>
([i]a, [j]a) -> [m + i]a
现在,那种类型看起来有点吓人;但本质上它是在告诉您可以给它任何大小的参数,并且它对任何其他大小都有效,只要新大小大于您给定的两个大小中的最大值。当然,这个推断的大小比你可能关心的要多态得多。所以你可以给它一些更具可读性的东西:
test : {m, n} (fin m, fin n) => [m] -> [n] -> [max m n]
test x y = (zero#x) ^ (zero#y)
我相信这完美地捕捉到了你的意图。请注意,cryptol 将如何确保您的输入是有限的,并且您会获得给定的两种大小中的最大值。
回到你的例子,Cryptol 告诉你要乘以 16,你至少需要 5 位,因此2>=5
是不可满足的。这有点神秘,但源于使用了多态类型的文字。您可以使用该zero
技巧以与以前相同的方式解决问题:
Cryptol> let shift (v, s:[2]) = v >>> ((zero#s)*16+8)
[warning] at <interactive>:1:32--1:38:
Defaulting type argument 'front' of '(#)' to 3
但请注意,cryptol 如何警告您zero
那里使用的类型,因为类型的>>>
多态性足以允许不同的大小移动/旋转:
Cryptol> :t (>>>)
(>>>) : {n, ix, a} (fin n, fin ix) => [n]a -> [ix] -> [n]a
在这些情况下,Cryptol 将通过查看表达式来选择默认的最小可能大小。不幸的是,它在这里做错了。3
通过为选择大小zero
,您将有一个5
位移,但您的表达式可以产生 的最大值3*16+8=56
,这需要至少 6 位来表示。请注意,Cryptol 仅使用处理乘法所需的最小大小,而不关心溢出!这就是为什么注意此类警告很重要的原因。
需要明确的是:Cryptol 根据关于类型推断如何工作的语言规则做了正确的事情,但它最终选择了一个对于你想要做的事情来说太小的尺寸。
所以,你应该写你shift
的如下:
Cryptol> let shift (v, s:[2]) = v >>> (((zero:[4])#s)*16+8)
Cryptol> :t shift
shift : {n, a} (fin n) => ([n]a, [2]) -> [n]a
这里重要的是确保表达式s*16+8
适合最终结果,并且由于s
只有 2 位宽,因此最大值将56
如上所述,它需要至少 6 位来表示。这就是为什么我选择[4]
.zero
这里的故事的寓意是,您应该始终明确您的位向量的大小,并且 Cryptol 将为您提供正确的框架,以多态方式表达您的约束,以允许代码重用而没有歧义,避免许多陷阱Verilog 和其他类似的语言。