3

I'd like to define a function as infix, so that users don't have to manually surround the function with backticks to call it. Specifically, I'm writing a DSL-like function that accepts a Rank and Suit and contructs a Poker card record:

-- 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,
  ...
  ]

I believe of is reserved as syntax for case ... of expressions, so I'd have to rename it something like of', .of, +of, etc.

mcandre
  • 22,868
  • 20
  • 88
  • 147

3 Answers3

10

There's no way to define a function with an alphanumeric name as infix. Haskell's syntax rules only allow for functions with symbolic names or function names surrounded with backticks to be used infix - there's no way to change that.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
6

Well, you might already know this, but (of course) operators /can/ be infix. So you could , instead r of s have r >| s.

Ferti
  • 61
  • 1
  • Indeed, operators *must* be infix. – dave4420 Mar 11 '13 at 20:10
  • 2
    @dave4420 Unless you wrap them in parentheses. – sepp2k Mar 11 '13 at 20:12
  • 3
    @sepp2k Sure, but that's morally the same as wrapping an alphanumeric identifier in backticks. You can't define a prefix symbolic operator (unless you wrap it in parens everywhere you use it), just as you can't define an infix alphanumeric operator (unless you wrap it in backticks everywhere you use it). – dave4420 Mar 11 '13 at 20:31
6

Here's a hacky solution with some extra typing, but without backticks! I first posted this on reddit, if that's okay.

I assume you have derived Enum for Rank.

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
   ]

The OF datatype isn't strictly necessary, but prevents confusing (but very metal) stuff like ace "Motorhead" Spades. You can still write ace undefined Spades, there's really no way around that, I think.

If of wasn't a keyword, you could even write of = OF.


There is also an utterly evil hack to get rid of the 'of' entirely, and using numbers for cards:

{-# LANGUAGE FlexibleInstances #-} -- this goes on top of your file

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

Now 2 Spades :: Card typechecks (but you need the explicit type!) and is what you think it is :-) However, I strongly advise you to not do this in serious code; but it looks kind of cool.

yatima2975
  • 6,580
  • 21
  • 42