2014-08-28 45 views
10

有没有什么办法可以消除表情简化带来的痛苦?学习Haskell - 如何简化表达式?

例如,假设这个表达式:

(+) <$> a <*> b $ 1 

我很想看到一个工具,可以解释这是什么意思。这对于初学者相当费力(发现在来源正确的实例函数定义,检查运算符优先级),以简化与所涉及的所有步骤的表达式:

fmap (+) a <*> b $ 1 

definitionData.Functor

(.) (+) a <*> b $ 1 

fmapControl.Monad.Instancesinstance Functor ((->) r)

等等。

编辑:为了澄清,我正在寻找一种使用实际函数定义重写表达式的方法,以便新手能够理解此表达式的结果。如何辨别(<$>) = fmap?我不知道如何使用hoogle和其他工具找到特定的实例定义(源代码)。

编辑:更改不正确的原始表达式以匹配下面的减少。

回答

3

开始ghci中,:cd你正在阅读的来源,:load模块你有兴趣,并使用:i命令来获取信息的基本目录:

ghci> :i <$> 
(<$>) :: Functor f => (a -> b) -> f a -> f b 
    -- Defined in `Data.Functor' 
infixl 4 <$> 
ghci> :i $ 
($) :: (a -> b) -> a -> b -- Defined in `GHC.Base' 
infixr 0 $ 
ghci> :i . 
(.) :: (b -> c) -> (a -> b) -> a -> c -- Defined in `GHC.Base' 
infixr 9 . 

告诉你的类型,它定义的位置,关联性(infixlinfixr)和优先级(数字;越高越严格)。所以(*10) <$> a $ 1被读作((*10) <$> a) $ 1

当您使用:load模块时,该模块内范围内的所有名称将在ghci中的范围内。如果你在代码中有错误,那么你不能在:i里面找到任何东西。在这些情况下,您可以对线条进行注释,使用undefined,并且可能还会使用behlkir建议的打孔(尚未与那些打针太多)。

当你在它的时候,尝试在ghci中的:?命令。

6

我发现,最简单的方式是使用typed holes提供GHCI 7.8:

> (*10) <$> _a $ 1 
Found hole ‘_a’ with type: s0 -> b 
Where: ‘s0’ is an ambiguous type variable 
     ‘b’ is a rigid type variable bound by 
      the inferred type of it :: b at <interactive>:4:1 
Relevant bindings include it :: b (bound at <interactive>:4:1) 
In the second argument of ‘(<$>)’, namely ‘_a’ 
In the expression: (* 10) <$> _a 
In the expression: (* 10) <$> _a $ 1 

所以这告诉我,a :: s0 -> b。其次是要弄清楚运营商的顺序:

> :i (<$>) 
(<$>) :: Functor f => (a -> b) -> f a -> f b 
infixl 4 <$> 
> :i ($) 
($) :: (a -> b) -> a -> b 
infixr 0 $ 

因此,这说$高度右结合,并赋予它的类型,我们看到它的第一个参数必须是一个函数,所以a必须是一个函数(双重确认)。这意味着(*10) <$> a $ 1((*10) <$> a) $ 1相同,所以我们首先关注(*10) <$> a

> :t ((*10) <$>) 
((*10) <$>) :: (Num a, Functor f) => f a -> f a 
> :t (<$> _a) 
Found hole ‘_a’ with type: f a 
Where: ‘a’ is a rigid type variable bound by 
      the inferred type of it :: (a -> b) -> f b at Top level 
     ‘f’ is a rigid type variable bound by 
      the inferred type of it :: (a -> b) -> f b at Top level 
In the second argument of ‘(<$>)’, namely ‘_a’ 
In the expression: (<$> _a) 

所以我们需要a是一个仿函数。什么是可用的实例?

> :i Functor 
class Functor (f :: * -> *) where 
    fmap :: (a -> b) -> f a -> f b 
    (<$) :: a -> f b -> f a 
     -- Defined in ‘GHC.Base’ 
instance Functor Maybe -- Defined in ‘Data.Maybe’ 
instance Functor (Either a) -- Defined in ‘Data.Either’ 
instance Functor ZipList -- Defined in ‘Control.Applicative’ 
instance Monad m => Functor (WrappedMonad m) 
    -- Defined in ‘Control.Applicative’ 
instance Control.Arrow.Arrow a => Functor (WrappedArrow a b) 
    -- Defined in ‘Control.Applicative’ 
instance Functor (Const m) -- Defined in ‘Control.Applicative’ 
instance Functor [] -- Defined in ‘GHC.Base’ 
instance Functor IO -- Defined in ‘GHC.Base’ 
instance Functor ((->) r) -- Defined in ‘GHC.Base’ 
instance Functor ((,) a) -- Defined in ‘GHC.Base’ 

所以(->) r恰好是一个,这是真棒,因为我们知道a必须是一个函数。从Num约束中,我们可以确定r必须与Num a => a相同。这意味着(*10) <$> a :: Num a => a -> a。从那我们然后申请1它,我们会得到(*10) <$> a $ 1 :: Num a,其中a是一些未知的功能。

所有这一切都可以使用GHCi使用:t:i,以及打字孔。当然,涉及的步骤相当多,但是当您试图分解复杂表达式时,它绝不会失败,只需查看不同子表达式的类型即可。

5

GHCi是非常正确的建议,我也建议。

我也想建议Hoogle,因为与即时搜索启用(在正确的顶侧栏有它的按钮),你可以搜索功能非常迅速,它可以提供很多,比GHCi更多的信息,最好的部分是你不必提及在其中搜索模块。这与您必须先导入的GHCi相反:

ghci> :t pure 
<interactive>:1:1: Not in scope: ‘pure’ 
ghci> :m +Control.Applicative 
ghci> :t pure 
pure :: Applicative f => a -> f a 

上面的Hoogle链接只是一个(来自Haskell.org站点)。 Hoogle是一个程序,您也可以在您的机器上安装(cabal install hoogle)并从命令行(hoogle your-query)执行查询。
旁注:您必须首先运行hoogle data来收集信息。它需要wget/curl,所以如果你在Windows上,你可能首先需要在你的路径中获得this(或者当然是用于Windows的curl)。在Linux上它几乎总是内置的(如果你没有在Linux上使用它,只需要apt-get它)。顺便说一下,我从来没有在命令行中使用Hoogle,但它不是那么容易访问,但它仍然非常有用,因为一些文本编辑器及其插件可以利用它。

或者您可以使用FPComplete's Hoogle这有时是更令人满意的(因为在我的经验,它已经意识到更多的第三方库。我只在那些“Hoogling会议”使用它)。

另外还有Hayoo!

在Hoogle你可能>的95%的时间不会有这样做,但+Module进口,如果由于某种原因,它没有被搜索(这是这种情况有时是第三方库)的模块。
您还可以通过-Module过滤模块。
例如:destroyTheWorld +World.Destroyer -World.Destroyer.Mercy找到destroyTheWorld,并确保你不看仁慈的方式做到这一点(这都非常方便与不同版本的函数的名称,像那些模块Data.ByteString & Data.ByteString.LazyData.Vector & Data.Vector.Mutable等)。

哦,Hoogle的另一个惊人的优势是它不仅可以显示函数的签名,还可以将您带到模块的Haddock页面,因此您还可以在这些页面获得文档+,可以点击在每个函数右侧的“源代码”上查看它是如何实现以获取更多信息的。

这超出了问题的范围,但Hoogle也用于查询功能签名,这只是.. mindblowingly有帮助。如果我想要一个带有索引号和列表的函数,并给出该索引中的元素,并且我想知道它是否已经内置,我可以在几秒钟内搜索它。
我知道函数接受一个数字和一个列表,并给我的列表的元素,因此函数签名必须看东西沿着这些路线:Int -> [a] -> a(或一般:Num a => a -> [b] -> b),并且这两种情况下出现,真的有一个函数((!!)genericIndex)。

凡GHCI有upperhand是抽象的功能,这意味着大量的交易时,你可以玩弄的表达,探索它们,等等很多倍。
能够:l(oad)非常有帮助。

如果您只是在寻找功能签名,您可以结合Hoogle和GHCi。
在GHCI你可以输入:! cmd,并GHCI将在命令行执行cmd,并打印结果。这意味着您也可以在GHCi中使用Hoogle,例如:! hoogle void