11

I've tried googling but come up short. I am furthering my Haskell knowledge by reading some articles and I came across one that uses a syntax I've never seen before. An example would be:

reconstruct node@(Node a b c l r) parent@(Node b d le ri)

I've never seen these @'s before. I tried searching online for an answer but came up short. Is this simply a way to embed tags to help make things clearer, or do they have an actual impact on the code?

jub0bs
  • 60,866
  • 25
  • 183
  • 186
Rewbert
  • 347
  • 1
  • 3
  • 11
  • 1
    Search for "as patterns" in http://learnyouahaskell.com/syntax-in-functions#pattern-matching for an introduction. – jub0bs May 19 '15 at 13:02
  • 8
    While traditional search engines won't return useful results when searching for special characters, Haskell's own [hoogle is usually quite useful](https://www.haskell.org/hoogle/?hoogle=%40) for such questions. – ComicSansMS May 19 '15 at 13:04
  • @ComicSansMS More helpful then the answer! – Dmitri Zaitsev Dec 26 '16 at 05:52
  • Does this answer your question? [Understanding the Haskell as-pattern](https://stackoverflow.com/questions/27467650/understanding-the-haskell-as-pattern) – Redu Nov 09 '19 at 16:59
  • @ComicSansMS, it doesn't help when searching for syntax though. – dfeuer Jan 25 '22 at 19:56

2 Answers2

23

It is used in pattern matching. Now node variable will refer to the entire Node data type for the argument Node a b c l r. So instead of passing to the function as Node a b c l r, you can use node instead to pass it up.

A much simpler example to demonstrate it:

data SomeType = Leaf Int Int Int | Nil deriving Show

someFunction :: SomeType -> SomeType
someFunction leaf@(Leaf _ _ _) = leaf
someFunction Nil = Leaf 0 0 0

The someFunction can also be written as:

someFunction :: SomeType -> SomeType
someFunction (Leaf x y z) = Leaf x y z
someFunction Nil = Leaf 0 0 0

See how simpler was the first version ?

Sibi
  • 47,472
  • 16
  • 95
  • 163
  • Even simpler would be `someFunction leaf@Leaf{} = leaf`. – András Kovács May 19 '15 at 13:52
  • 2
    @AndrásKovács, but doesn't it require some explanations on `Leaf{}`? – d12frosted May 19 '15 at 13:54
  • The 2 are slightly different in ghci (in the second one it will re-apply the constructor arguments), but I'd be very surprised if that were the case in ghc compiled code – Jeremy List May 20 '15 at 06:10
  • What does `@` mean in a context like `Proxy @Foo`? – Josh.F Nov 04 '18 at 18:51
  • 1
    Whew, finally figured out it needs `{-# LANGUAGE TypeApplications #-}`, still learning what it is though. – Josh.F Nov 04 '18 at 18:57
  • @Josh.F - In that context, it is some sort of type cast à la C/C++. I would expect your `Foo` thing to be a _type_. Formally, this @_type_ notation allows you to give explicit type arguments to a polymorphic function. Like: `let x = (read @Integer "33")` setting x to be an Integer object of value 33. [Details here](https://gitlab.haskell.org/ghc/ghc/wikis/type-application). – jpmarinier Nov 09 '19 at 22:20
  • 1
    @jpmarinier, no, it is not a type cast of any sort. It does indeed let you apply type arguments, but what those mean depend on the particular functions or methods involved. – dfeuer Nov 09 '19 at 23:10
  • @dfeuer I agree it is only a loose analogy, as a C/C++ _cast_ applies to a value, while this Haskell construct applies to a function, with for example the expression `(read @Integer)` having type `String -> Integer`. Please note I did not use the word _cast_ in my answer below. – jpmarinier Nov 09 '19 at 23:54
  • 1
    @jpmarinier, it's so loose I can't even guess what it means. `foo @Int` could easily be a function that takes two characters and produces an `IO` action returning `()`. – dfeuer Nov 10 '19 at 00:37
12

Using @t as a type indicator

Besides the argument pattern matching usage described in the answer of @Sibi, in Haskell the "at" character ('@', also known as an arobase character) can be used in some contexts to force a typing decision. This is mentioned in the comments by @Josh.F.

This is not part of the default language features, and is known as the Type Application Haskell language extension. In summary, the extension allows you to give explicit type arguments to a polymorphic function such as read. In a classic .hs source file, the relevant pragma must be included:

{-#  LANGUAGE TypeApplications  #-}

Example:

$ ghci
GHCi, version 8.2.2: http://www.haskell.org/ghc/  :? for help
 λ> 
 λ> let x = (read @Integer "33")

 <interactive>:4:10: error:
    Pattern syntax in expression context: read@Integer
    Did you mean to enable TypeApplications?
 λ> 
 λ> :set -XTypeApplications
 λ>
 λ> let x = (read @Integer "33")
 λ>
 λ> :type  x
 x :: Integer
 λ> 
 λ> x
 33
 λ> 

Further details

For the read polymorphic function, the type indicator introduced by @ relates to the type of the result returned by read. But this is not generally true.

Generally speaking, you have to consider the type variables that appear in the type signature of the function at hand. For example, let's have a look at the fmap library function.

fmap :: Functor ft => (a -> b) -> ft a -> ft b

So here, we have 3 type variables, in order of appearance: ft, a, b. If we specialize fmap like this:

myFmap = fmap  @type1  @type2  @type3

then type1 will relate to ft, type2 will relate to a and type3 will relate to b. Also, there is a special dummy type indicator @_ which means: “here, any type goes”.

For example, we can force the output type of fmap to be Integer and the functor to be the plain list [], leaving the input type a unspecified:

 λ> 
 λ> myFmap = fmap  @[]  @_  @Integer
 λ> 
 λ> :type myFmap
 myFmap :: (_ -> Integer) -> [_] -> [Integer]
 λ> 

As for the read function, its type is:

read :: Read a => String -> a

So there is only room for one type indicator, and it relates to the type of the result returned by read, as displayed above.

jpmarinier
  • 4,427
  • 1
  • 10
  • 23
  • This is exactly what I was looking - I knew about @'s use in pattern matching, but not for type indication. Thanks! – Brent Pappas May 06 '22 at 17:46