如何在Haskell中部分定义函数签名?

Wiz*_*zek 46 haskell

初始点:

fn :: [a] -> Int
fn = (2 *) . length
Run Code Online (Sandbox Code Playgroud)

比方说,我们只是想约束的返回值,那么我们可以这样写:

fn list = (2 * length list) :: Int
Run Code Online (Sandbox Code Playgroud)

如何限制参数?简单.

fn list = 2 * length (list :: [Char])
Run Code Online (Sandbox Code Playgroud)

虽然这有效,但最好是收集顶部的签名而不是散布在函数体周围.

这是我能得到的最接近的:

fnSig = undefined :: [Char] -> a
fn | False = fnSig
   | True  = (* 2) . length
Run Code Online (Sandbox Code Playgroud)

基于http://okmij.org/ftp/Haskell/partial-signatures.lhs通过http://okmij.org/ftp/Haskell/types.html#partial-sigs

但是,我想要一个更清洁的解决方案.更好地沟通的东西,我的意图是部分限制.像这样的东西,例如:

fn :: [Char] -> a
fn = (2 *) . length
Run Code Online (Sandbox Code Playgroud)

或者可能:

fn :: [Char] -> _
fn = (2 *) . length
Run Code Online (Sandbox Code Playgroud)

这可能吗?

编辑以进一步说明:

@GaneshSittampalam在下面的评论中提到了一个重点.我正在寻找"在没有任何类型的签名和必须给出一个精确的签名之间的中间房子".所以,我不是在寻找基于TypeClass的答案,我只是希望GHC填写我的函数的未指定(或不完全受限)类型的空白.

编辑以响应@WillNess

是的,像这样......

fn list = 2 * length list
  where
    _ = list :: [Char]
Run Code Online (Sandbox Code Playgroud)

...可以工作,但仅适用于参数,并且只有在函数不是无点的情况下才有效.有没有办法将此技术应用于无点函数或返回值?

编辑以响应@Rhymoid

我受到了启发,并在@ Rhymoid的想法中玩弄了一下,然后想出了这个:

fn = (2 *) . length
  where
    _ = fn `asTypeOf` (undefined :: [Char] -> a)
    _ = fn `asTypeOf` (undefined :: a -> Int)
    _ = fn `asTypeOf` (undefined :: a -> b)
    _ = fn `asTypeOf` (undefined :: a)
Run Code Online (Sandbox Code Playgroud)

此方法还限制fn的类型签名,并且不会污染任何名称空间.

通常我们只有一条asTypeOf线,我只是添加了多条线来展示这种方法有多强大.

它比我想要的更笨拙,但我想即使没有语言的特定语法支持,我们也可以做到这一点.

@Rhymoid,如果您也喜欢,请将其添加到您的答案中.:)

Dom*_*ese 36

对于自我推销感到抱歉,但正是这个特色是博士最近的一篇论文的主题.学生Thomas Winant,我自己,Frank Piessens和Tom Schrijvers,最近由Thomas在2014年PADL研讨会上发表.请参阅此处查看完整的论文.这是一种已经存在于其他语言中的功能,但与Haskell GADT等功能的交互使其足够有趣,可以解决细节问题.

托马斯正致力于GHC的实施.自撰写论文以来,它已得到进一步改进,但在GHC中实施"通配符约束"在技术上比我们预期的要困难一些.我们希望能够进一步开展工作并与GHC开发人员联系以获得它,但是否这种情况发生可能取决于人们希望在Haskell中拥有该功能的程度......

更新14-4-2015:经过Thomas的大量工作以及SPJ和其他GHC人员的意见,GHC 7.10中已发布部分类型签名.Thomas Winant撰写了一篇关于如何使用它们的介绍性博客文章.

  • 好问题.我们还没有看过那种情况,但我希望在这种情况下,我们应该将设置应用于部分签名.无论如何,目前的计划是首先看看GHC开发人员是否对此功能有足够的兴趣,然后再努力研究这类问题.(可能)与ScopedTypeVariables,RankNTypes,ConstraintKinds等其他功能的交互也有待研究,例如...... (2认同)

小智 13

我一直在寻找一种方式来说' x'的类型与T' 结合'.Will Ness和chi给出的解决方案与我提出的解决方案非常接近,但是有一种方法可以在Haskell 98中完成,而无需屠宰自己的功能.

-- Your function, without type signature.
fn = (2 *) . length

-- The type signature, without actual definition.
fnTy :: [Char] -> a
fnTy = undefined

-- If this type checks, then the type of 'fn' can be unified 
--                                      with the type of 'fnTy'.
fn_unifies_with_type :: ()
fn_unifies_with_type = let _ = fn `asTypeOf` fnTy in ()
Run Code Online (Sandbox Code Playgroud)

你甚至可以去做

fn = (2 *) . length
  where
    _ = fn `asTypeOf` (undefined :: [Char] -> a)
Run Code Online (Sandbox Code Playgroud)

  • 这也有效,而且要短得多:)你甚至可以选择`_ = fn \`asTypeOf \`(undefined :: [Char] - > a)`. (4认同)
  • 我喜欢这个``_ = fn`asTypeOf`(undefined :: [Char] - > a)``.看起来整洁. (2认同)

aug*_*tss 10

你正在寻找我们很多人都想要的功能,但Haskell没有.也不是.你想要一种部分类型的签名.建议的符号是

fn :: [Char] -> _
fn = (2*) . length
Run Code Online (Sandbox Code Playgroud)

_意思是"这里有一个类型,但我也懒得写出来".

它看起来像是一个非常容易实现的功能(_在签名中使用统一变量进行实例化),但是没有人愿意计算出语义细节以及与其他功能的交互.

  • 我认为漏洞位于表达层 - 你在表达式中编写`_foo`,编译器会告诉你它应该具有什么类型:http://www.haskell.org/haskellwiki/GHC/TypeHoles (3认同)
  • @Wizek SPJ表示,如果有人向他展示了打字规则是什么(并且它看起来合理),他会添加它. (3认同)

Wil*_*ess 6

要仅指定参数的类型,您可以编写类似的内容

fn list = 2 * length list
  where
    a :: [Char]
    a = list `asTypeOf` a
Run Code Online (Sandbox Code Playgroud)

因此,以后很容易修改它,例如,

fn list = 2 * fromIntegral (length list)
  where
    a :: [Char]
    a = list `asTypeOf` a
Run Code Online (Sandbox Code Playgroud)

并相应地改变其推断类型:

*Main> :t fn
fn :: [Char] -> Int
*Main> :r
-- changed file reloaded
*Main> :t fn
fn :: (Num t) => [Char] -> t
Run Code Online (Sandbox Code Playgroud)

您可以使用相同的扭曲技术来指定函数的返回类型,可能以样式定义,但这并不漂亮.

fn2 list = r
  where
    r :: Int
    r = f list
    f = (2 *) . length
Run Code Online (Sandbox Code Playgroud)

它与您现在拥有的内容没有太大差别,只需将代码和类型规范分开.

  • 谢谢你的`asTypeOf`,不知道它. (2认同)