解释算术


9

一个鲜为人知的事实是,如果您打开足够多的语言扩展(ghc),Haskell会成为一种动态类型的解释语言!例如,以下程序实现加法。

{-# Language MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances #-}

data Zero
data Succ a

class Add a b c | a b -> c
instance Add Zero a a
instance (Add a b c) => Add (Succ a) b (Succ c)

看起来真的不再像Haskell了。对于一个而不是对对象进行操作,我们对类型进行操作。每个数字都是它自己的类型。除了函数,我们有类型类。函数依赖使我们可以将它们用作类型之间的函数。

那么我们如何调用我们的代码?我们使用另一类

class Test a | -> a
 where test :: a
instance (Add (Succ (Succ (Succ (Succ Zero)))) (Succ (Succ (Succ Zero))) a)
  => Test a

这将类型设置为test4 +3。如果我们在ghci中打开它,我们将发现test确实是类型7:

Ok, one module loaded.
*Main> :t test
test :: Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero))))))

任务

我希望您实现一个将两个Peano数字(非负整数)相乘的类。在上面的示例中,将使用相同的数据类型构造Peano数字:

data Zero
data Succ a

您的课程也将以与上述相同的方式进行评估。您可以根据自己的喜好为班级命名。

您可以免费使用任何ghc语言扩展。

测试用例

这些测试用例假定您的类名为M,如果愿意,您可以使用其他名称。

class Test1 a| ->a where test1::a
instance (M (Succ (Succ (Succ (Succ Zero)))) (Succ (Succ (Succ Zero))) a)=>Test1 a

class Test2 a| ->a where test2::a
instance (M Zero (Succ (Succ Zero)) a)=>Test2 a

class Test3 a| ->a where test3::a
instance (M (Succ (Succ (Succ (Succ Zero)))) (Succ Zero) a)=>Test3 a

class Test4 a| ->a where test4::a
instance (M (Succ (Succ (Succ (Succ (Succ (Succ Zero)))))) (Succ (Succ (Succ Zero))) a)=>Test4 a

结果

*Main> :t test1
test1
  :: Succ
       (Succ
          (Succ
             (Succ
                (Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero)))))))))))
*Main> :t test2
test2 :: Zero
*Main> :t test3
test3 :: Succ (Succ (Succ (Succ Zero)))
*Main> :t test4
test4
  :: Succ
       (Succ
          (Succ
             (Succ
                (Succ
                   (Succ
                      (Succ
                         (Succ
                            (Succ
                               (Succ
                                  (Succ
                                     (Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero)))))))))))))))))

打字技术采访中汲取灵感


语言扩展免费吗?如果是这样的话?
Potato44 '18年

@ Potato44哦,是的。所有语言扩展都是免费的。
Ad Hoc Garf Hunter,

1
嘿...尽管不是,但这篇帖子似乎很模因。
魔术章鱼缸

Answers:


9

130个 121字节

-9个字节,感谢ØrjanJohansen

type family a+b where s a+b=a+s b;z+b=b
type family a*b where s a*b=a*b+b;z*b=z
class(a#b)c|a b->c
instance a*b~c=>(a#b)c

在线尝试!

这定义了用于加法(+)和乘法的封闭类型族(*)。然后(#)定义一个类型类,该类型类使用(*)类型族以及等式约束从familes类型的世界转换为typeclass prolog的世界。


3
如果交换方程式,则可以替换Zeroz
与Orjan约翰森

1
@ØrjanJohansen完成。我为某人保存了9个字节,为我保存了9个字节。
Potato44 '18

我不知道如何使用类型族,但是也许像这样的函数+有用,所以您不需要定义是否有用?
林恩

@Lynn最后出来的时间更长。TIO
Potato44 '18

1
@WheatWizard我刚刚意识到我在评论中发布的代码,因为它发布的时间更长,本质上是答案的尾部递归版本。
Potato44

6

139字节

class(a+b)c|a b->c;instance(Zero+a)a;instance(a+b)c=>(s a+b)(s c)
class(a*b)c|a b->c;instance(Zero*a)Zero;instance((a*b)c,(b+c)d)=>(s a*b)d

在线尝试!

定义一个类型运算符*。相当于Prolog程序:

plus(0, A, A).
plus(s(A), B, s(C)) :- plus(A, B, C).
mult(0, _, 0).
mult(s(A), B, D) :- mult(A, B, C), plus(B, C, D).

Potato44和Hat Wizard分别节省了9个字节。谢谢!


您无需将数据声明计数为字节总数。如果有机会,我会在问题中更清楚地说明这一点
Ad Hoc Garf Hunter

另外,我认为您可以使用通用f代替Succ
Ad Hoc Garf Hunter,

1
抛开冒号可以节省9个字节。
Potato44

我认为Hat Wizard还保存了9个,而不是6个。发生了3次Succ。
Potato44 '18

1

家庭版本,115字节

type family(a%b)c where(a%b)(s c)=s((a%b)c);(s a%b)z=(a%b)b;(z%b)z=z
class(a#b)c|a b->c
instance(a%b)Zero~c=>(a#b)c

在线尝试!

这是使用像potato44的封闭型家庭。除了与其他答案不同,我仅使用一种类型的家庭。

type family(a%b)c where
  -- If the accumulator is Succ c:
  -- the answer is Succ of the answer if the accumulator were c
  (a%b)(s c)=s((a%b)c)
  -- If the left hand argument is Succ a, and the right hand is b
  -- the result is the result if the left were a and the accumulator were b
  (s a%b)z=(a%b)b
  -- If the left hand argument is zero
  -- the result is zero
  (z%b)z=z

这定义了三种类型的运算符。它实质上实现了(a*b)+c。每当我们要将右手参数添加到总数中时,我们就会将其放在累加器中。

这使我们(+)根本不需要定义。从技术上讲,您可以使用该系列来实现加法

class Add a b c | a b -> c
instance (Succ Zero % a) b ~ c => Add a b c

类版本,137字节

class(a#b)c d|a b c->d
instance(a#b)c d=>(a#b)(f c)(f d)
instance(a#b)b d=>(f a#b)Zero d
instance(Zero#a)Zero Zero
type(a*b)c=(a#b)Zero c

在线尝试!

此类版本比家庭版本有所失落,但仍比此处的最短版本短。它使用与我的家庭版本相同的方法。


很好,我看到您的类型族在数学上实现了a * b + c。提到“划分”是否意味着要“增加”?
Potato44 '19

顺便说一句,您碰巧此时违反了自己的规范。“实现一个将两个Peano数字相乘的类”您当前拥有的不是一个类,Constraint尽管它确实是一种。因此,您应该更新规范或恢复为使用类而不是类型同义词的形式。如果我使用类型同义词,则我的答案可以减少到96个字节,因此它比我节省了一个字节
Potato44

@ Potato44我给人的印象是,一门课只是某种导致矛盾的东西。也许是由于问题不明确。然后,我将回到115的答案。
Ad Hoc Garf Hunter,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.