如何在Haskell中定义中缀函数?

3

我希望定义一个中缀函数,这样用户就不必手动用反引号将函数括起来才能调用它。具体来说,我正在编写一个类似DSL的函数,它接受一个Rank和Suit,构造一个Poker卡记录:

-- pseudocode
data PokerCard = PokerCard { rank :: Rank, suit :: Suit } deriving (Eq)

of :: Rank -> Suit -> PokerCard
r of s = PokerCard { rank = r, suit = s }

pokerDeck = [
  Ace of Spades,
  Two of Spades,
  ...
  ]

我认为of被保留作为case ... of表达式的语法,所以我必须将其重命名为of'.of+of等。


请参阅 https://dev59.com/uGkv5IYBdhLWcg3wgRCc - Matt Fenwick
2
另外:https://dev59.com/LGox5IYBdhLWcg3wNRtj#9356814 - amindfv
3个回答

10

无法使用字母数字名称定义中缀函数。Haskell的语法规则仅允许使用符号名称或用反引号括起来的函数名称作为中缀函数 - 无法更改此规则。


6

也许你已经知道了,但是运算符/可以/作为中缀操作符。所以,你可以使用r >| s代替r of s


确实,运算符必须是中缀的。 - dave4420
2
@dave4420 除非你将它们用括号括起来。 - sepp2k
3
@sepp2k 当然可以,但这在道德上与用反引号包装字母数字标识符是一样的。你不能定义一个前缀符号运算符(除非你在使用它的每个地方都用括号包装它),就像你不能定义一个中缀字母数字运算符(除非你在使用它的每个地方都用反引号包装它)一样。 - dave4420

6
这是一个有点取巧的解决方案,需要打些额外的字,但不需要使用反引号!我最初是在 Reddit 上发布的,如果可以的话。
我假设你已经为 Rank 派生了 Enum。
data OF = OF
ace :: OF -> Suit -> PokerCard
ace _ s = PokerCard Ace s

-- or point-free
two :: OF -> Suit -> PokerCard
two _ = PokerCard Two

-- or with const
three :: OF -> Suit -> PokerCard
three = const (PokerCard Three)

-- you get the idea!
-- the rest in one line:
four,five,six,seven,eight,nine,ten,jack,king :: OF -> Suit -> PokerCard
[four,five,six,seven,eight,nine,ten,jack,king] = map (const . PokerCard) [Four .. King]

 -- now you can write
 pokerDeck = [
   ace OF Spades, two OF Spades -- and so on
   ]

OF数据类型并非必需,但可以避免出现令人困惑(但非常技术)的内容,例如ace "Motorhead" Spades。您仍然可以编写ace undefined Spades,我认为这真的没有办法避免。
如果of不是一个关键字,您甚至可以编写of = OF
还有一种极其恶劣的黑客方式来完全摆脱'of'这个词,使用数字代替卡片:
{-# LANGUAGE FlexibleInstances #-} -- this goes on top of your file

instance Num (Rank -> Suit) where
  fromInteger n = (undefined : map Card[Ace .. King]) !! (fromInteger n)

现在,2 Spades :: Card 类型检查通过(但您需要显式类型!)并且它就是您想要的 :-) 但是,我强烈建议您不要在正式的代码中这样做;但它看起来很酷。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接