【问题标题】:Understanding `~` with 2 Functions用 2 个函数理解 `~`
【发布时间】:2017-04-27 13:00:33
【问题描述】:

背景:我不明白 ~ 并正在请求一个用例。

给定:

{-# LANGUAGE GADTs #-}

f :: a ~ b => a -> b -> b
f a b = a

g :: a -> a -> a
g a b = a

在我看来,这两个功能是相等的:

Prelude> :r
[1 of 1] Compiling Main             ( TypeEq.hs, interpreted )
Ok, modules loaded: Main.
*Main> f 10 20
10
*Main> g 10 20
10

在什么情况下使用f而不是g有用?

【问题讨论】:

  • 确实,在这种情况下没有任何优势:这些功能是平等的。我认为,您需要诸如类型族或 GADT 之类的东西才能看到一些有用的东西。或者,也许 $Data.Typeable.eqT$ 可以作为一个很好的例子。
  • 如果您想知道如何以一种非常有用的方式使用 ~,请阅读 Chris Done 的这篇精彩博文:chrisdone.com/posts/haskell-constraint-trick

标签: haskell


【解决方案1】:
{-# LANGUAGE TypeFamilies #-}

import GHC.Exts (IsList(..))

fizzbuzz :: (IsList l, Item l ~ Int) => l -> IO ()
fizzbuzz = go . toList
 where go [] = return ()
       go (n:m)
        | n`mod`3==0  = putStrLn "fizz" >> go m
        | n`mod`5==0  = putStrLn "buzz" >> go m
        | otherwise   = print n >> go m

然后

Prelude> fizzbuzz [1..7]
1
2
fizz
4
buzz
fizz
7
Prelude> import Data.Vector.Unboxed as UA
Prelude UA> fizzbuzz (UA.fromList[1..7] :: UA.Vector Int)
1
2
fizz
4
buzz
fizz
7

您现在可能会反对,最好使用Foldable 约束来完成,而不是丑陋地转换为列表。实际上这是做不到的,因为未装箱的向量由于Unbox 约束而没有可折叠的实例!

然而,它也可以用非等式约束来完成,即

fizzbuzz :: (IsList l, Num (Item l), Eq (Item l), Show (Item l))
     => l -> IO ()

这更笼统,但可以说也更尴尬。当您实际上只需要一个包含类型时,等式约束可能是一个不错的选择。

确实,如果有点重复的话,我有时会发现为了使类型签名更简洁而加入等式约束很方便:签名

complicatedFunction :: Long (Awkward (Type a) (Maybe String))
                 -> [Long (Awkward (Type a) (Maybe String))]
                 -> Either String (Long (Awkward (Type a) (Maybe String)))

可以替换为

complicatedFunction :: r ~ Long (Awkward (Type a) (Maybe String))
             => r -> [r] -> Either String r

这可能比其他 DRY 可能性更好

type LAwkTS a = Long (Awkward (Type a) (Maybe String))

complicatedFunction :: LAwkTS a -> [LAwkTS a] -> Either String (LAwkTS a)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-31
    • 2019-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-10
    • 1970-01-01
    相关资源
    最近更新 更多