在某种程度上,这是我之前的问题的一个倒退,但是...有人可以提醒我为什么这不起作用?
class Container c e where empty :: c insert :: e -> c -> c instance Container [x] x where empty = [] insert = (:) instance Container ByteString Word8 where empty = BIN.empty insert = BIN.cons instance Ord x => Container (Set x) x where empty = SET.empty insert = SET.insert
显然,如果这很容易,没有人会想要发明功能依赖或相关类型.那么上面的问题是什么?
假设我们有以下内容:
data Foo x from_list :: [x] -> Foo x to_list :: Foo x -> [x]
假设我想声明instance (Show x) => Show (Foo x)显示一个值产生适当的调用from_list.我到底该怎么做?特别是,我如何实现showsPrec以满足trixy fiddly优先规则?(也就是说,当且仅在必要时将表达式放在括号中.)
我发现自己写了很多代码
putStr "foo (bar 1) (bar 2) =" print $ foo (bar 1) (bar 2)
麻烦的是,打印的消息可能与实际执行的代码不同步.显而易见的解决方案是自动生成此代码.
一种方法是将所有文本放在一个文件中,然后编写一个小程序来读取文件并从中生成Haskell源代码.但另一种选择是使用Template Haskell.
有没有人知道如何编写一个函数来获取String并从中生成上述代码?我猜它应该很容易,但TH没有很好的记录.
似乎Frege关于类型类的想法与Haskell有很大不同.特别是:
语法似乎不同,没有明显的原因.
函数类型不能包含类实例.(似乎是一个相当奇怪的规则......)
语言规范说明了在子类实例声明中实现超类.(但如果你有钻石继承,那就不行了......这不会是一个错误,但它不能保证以某种方式工作?)
弗雷格对实例的看法不那么挑剔.(允许输入别名,类型变量不需要是不同的,等等)
方法可以声明为native,虽然还不完全清楚这是什么意思.
您似乎可以编写type.method访问方法.同样,没有迹象表明这意味着什么或为什么它有用.
子类声明可以为超类方法提供默认实现.(?)
简而言之,如果知道这些东西的人可以写出这些东西如何工作的解释将是有用的.它在语言规范中列出,但描述有点简洁.
(关于语法:我认为Haskell的实例语法更合乎逻辑."如果X是Y和Z的实例,那么它也是以下列方式的Q实例......"Haskell的类语法总是看起来有点奇怪对我来说.如果X实现Eq,这并不意味着它实现Ord,它暗示它可以实现,Ord如果它想要.我不知道什么是更好的符号将...)
Per Ingo的回答:
例如,假设Foo是一个超类Bar.假设每类有三种方法(foo1,foo2,foo3,bar1,bar2,bar3),并Bar提供了一个默认的实现foo1.这应该意味着
instance Bar FB where foo2 = ... foo3 = ... bar1 = ... bar2 = ... bar3 = ...
应该管用.但是这会有用吗:
instance Foo FB where foo2 = ... foo3 = ... instance Bar …
我有一个QuickCheck属性,如下所示:
prop42 :: Foo -> Bool
prop42 foo = fn1 foo == fn2 foo
Run Code Online (Sandbox Code Playgroud)
如果此属性失败,它将打印出来的内容foo.但我真的很想知道什么fn1并且fn2回来了.如果foo很大,手动生成这些信息有点不重要.(即,坐在那里,手动输入打印到Windows控制台窗口的巨大文本块.)
测试框架有一个比较相等的东西,并且如果相等不成立则打印出两个值是很常见的.但我似乎无法为QuickCheck找到这样的功能......
好的,所以这是一个愚蠢的问题.我已经定义了一个新的#运算符,我正在试图找出固定性声明应该是什么.
我想#在之后 申请++.这是否意味着优先级应高于或更低的比++?(++优先5.)
类型是(#) :: Foo -> Bar -> Foo.我想要左联想还是右联想?因为在我看来,其中一个会进行x # y # z类型检查,而另一个则不会.
我知道这可能看起来微不足道,但我总是总是似乎得到这个错误...
好的,所以这是一个不起眼的语言角落:
Haskell允许您导出标识符,该标识符的类型签名提到未导出的类型.究竟是什么语义?
例如,假设我有
module Foobar (bar) where
data Foo = ...
bar :: String -> Foo
Run Code Online (Sandbox Code Playgroud)
Foo不会导出,bar而是.但是bar提到的类型Foo.许多编程语言都不允许你这样做,但Haskell确实如此.
所以现在怎么办?似乎我可以打电话bar,但是结果不能做多少.特别是,我(大概)不能说出结果类型的名称,这有点奇怪.据推测,如果模块导出了一些Foo作为输入的函数,我应该能够将那些以我的结果作为输入的函数调用...但是在类型签名中我不能这么说.
当然,做上述事情并不是一个好主意 ; 我不打算在实际代码中这样做.我只是好奇它实际上做了什么.
假设这Parser x是一个解析的解析器x.这个解析器可能拥有一个many组合器,可以解析零次或多次出现的事情(当项解析器失败时停止).
如果Parser形成一个monad ,我可以看到如何实现它.如果Parser只是一个Applicative Functor,我无法弄清楚如何做到这一点.似乎没有任何方法可以检查以前的结果并决定下一步该做什么(正是monad添加的概念).我错过了什么?
Haskell 2010报告的第5.2节涉及模块导出列表.有一次,它说:
导出列表中的实体可以命名如下:
- 无论是在模块体中声明还是导入的值,字段名或类方法都可以通过将值的名称作为qvarid来命名,该值必须在范围内.操作符应括在括号中,以将它们转换为qvarids.
...
但是,呃......我错过了什么吗?因为根据第10章中的语法参考:
qvarid →[ monid.] varid
varid →(小 { 小 | 大 | 数字 |'})
那么宇宙中将运算符放入括号中会将其转换为qvarid?它看起来像一个运算符显然是一个varsym(或可能是qvarsym).
有谁知道这里发生了什么?我的意思是,显然 Haskell肯定支持在导出列表中编写运算符,但报告中的语法描述似乎没有意义......
所以我正在编写一个复杂的解析器,只使用Applicative(有问题的解析器甚至根本没有实现Monad).
对于普通的解析器,这很容易.对于非平凡的......不是那么多.应用界面似乎猛烈地迫使你以无点样式编写所有内容.这是非常难以处理的.
例如,考虑一下:
call = do
n <- name
char '('
trim
as <- sepBy argument (char ',' >> trim)
char ')'
trim
char '='
r <- result
return $ Call {name = n, args = as, result = r}
Run Code Online (Sandbox Code Playgroud)
现在让我们尝试使用applicative来编写:
call =
(\ n _ _ as _ _ _ _ r -> Call {name = n, args = as, result = r}) <$>
name <*>
char '(' <*>
trim <*>
sepBy argument (const const () <$> char ',' …Run Code Online (Sandbox Code Playgroud) haskell ×9
types ×3
applicative ×2
parsing ×2
class ×1
containers ×1
frege ×1
quickcheck ×1
syntax ×1