How does the :: operator syntax work in the context of bounded typeclass?

haskell deriving
haskell data
haskell data constructor
haskell instance
haskell newtype
haskell functions
haskell type signature
haskell ord

I'm learning Haskell and trying to understand the reasoning behind it's syntax design at the same time. Most of the syntax is beautiful.

But since :: normally is like a type annotation, How is it that this works:

Input: minBound::Int

Output: -2147483648

There is no separate operator: :: is a type annotation in that example. Perhaps the best way to understand this is to consider this code:

main = print (f minBound)

f :: Int -> Int
f = id

This also prints -2147483648. The use of minBound is inferred to be an Int because it is the parameter to f. Once the type has been inferred, the value for that type is known.

Now, back to:

main = print (minBound :: Int)

This works in the same way, except that minBound is known to be an Int because of the type annotation, rather than for some more complex reason. The :: isn't some binary operation; it just directs the compiler that the expression minBound has the type Int. Once again, since the type is known, the value can be determined from the type class.

Making Our Own Types and Typeclasses, By using record syntax to create this data type, Haskell automatically made these Usually we use them when our data type would work regardless of the type of instance of any of the following typeclasses: Eq, Ord, Enum, Bounded, Show, Read. Haskell can derive the behavior of our types in these contexts if we use the​  What is a Context Bound? Context bounds were introduced in Scala 2.8.0, and are typically used with the so-called type class pattern, a pattern of code that emulates the functionality provided by Haskell type classes, though in a more verbose manner. A context bound requires a parameterized type, such as Ordered[A], but unlike String.

:: still means "has type" in that example.

There are two ways you can use :: to write down type information. Type declarations, and inline type annotations. Presumably you've been used to seeing type declarations, as in:

plusOne :: Integer -> Integer
plusOne = (+1)

Here the plusOne :: Integer -> Integer line is a separate declaration about the identifier plusOne, informing the compiler what its type should be. It is then actually defined on the following line in another declaration.

The other way you can use :: is that you can embed type information in the middle of any expression. Any expression can be followed by :: and then a type, and it means the same thing as the expression on its own except with the additional constraint that it must have the given type. For example:

foo = ('a', 2) :: (Char, Integer)
bar = ('a', 2 :: Integer)

Note that for foo I attached the entire expression, so it is very little different from having used a separate foo :: (Char, Integer) declaration. bar is more interesting, since I gave a type annotation for just the 2 but used that within a larger expression (for the whole pair). 2 :: Integer is still an expression for the value 2; :: is not an operator that takes 2 as input and computes some result. Indeed if the 2 were already used in a context that requires it to be an Integer then the :: Integer annotation changes nothing at all. But because 2 is normally polymorphic in Haskell (it could fit into a context requiring an Integer, or a Double, or a Complex Float) the type annotation pins down that the type of this particular expression is Integer.

The use is that it avoids you having to restructure your code to have a separate declaration for the expression you want to attach a type to. To do that with my simple example would have required something like this:

two :: Integer
two = 2

baz = ('a', two)

Which adds a relatively large amount of extra code just to have something to attach :: Integer to. It also means when you're reading bar, you have to go read a whole separate definition to know what the second element of the pair is, instead of it being clearly stated right there.


So now we can answer your direct question. :: has no special or particular meaning with the Bounded type class or with minBound in particular. However it's useful with minBound (and other type class methods) because the whole point of type classes is to have overloaded names that do different things depending on the type. So selecting the type you want is useful!

minBound :: Int is just an expression using the value of minBound under the constraint that this particular time minBound is used as an Int, and so the value is -2147483648. As opposed to minBound :: Char which is '\NUL', or minBound :: Bool which is False.

None of those options mean anything different from using minBound where there was already some context requiring it to be an Int, or Char, or Bool; it's just a very quick and simple way of adding that context if there isn't one already.


It's worth being clear that both forms of :: are not operators as such. There's nothing terribly wrong with informally using the word operator for it, but be aware that "operator" has a specific meaning in Haskell; it refers to symbolic function names like +, *, &&, etc. Operators are first-class citizens of Haskell: we can bind them to variables1 and pass them around. For example I can do:

(|+|) = (+)

x = 1 |+| 2

But you cannot do this with ::. It is "hard-wired" into the language, just as the = symbol used for introducing definitions is, or the module Main ( main ) where syntax for module headers. As such there are lots of things that are true about Haskell operators that are not true about ::, so you need to be careful not to confuse yourself or others when you use the word "operator" informally to include ::.


1 Actually an operator is just a particular kind of variable name that is applied by writing it between two arguments instead of before them. The same function can be bound to operator and ordinary variables, even at the same time.

A Gentle Introduction to Haskell: Classes, Numeric operators such as + are often defined to work on many different kinds of For example, let's define a type class containing an equality operator: If the context were omitted from the instance declaration, a static type error would result​. use special syntax can be written in an alternative style which allows currying. Operators that are in the same cell (there may be several rows of operators listed in a cell) are grouped with the same precedence, in the given direction. An operator's precedence is unaffected by overloading. The syntax of expressions in C and C++ is specified by a phrase structure grammar. The table given here has been inferred from the grammar.

Just to add another example, with Monads you can play a little like this:

import Control.Monad

anyMonad :: (Monad m) => Int -> m Int
anyMonad x = (pure x) >>= (\x -> pure (x*x)) >>= (\x -> pure (x+2))

$>  anyMonad 4 :: [Int]
=> [18]
$>   anyMonad 4 :: Either a Int
=> Right 18
$>   anyMonad 4 :: Maybe Int
=> Just 18

it's a generic example telling you that the functionality may change with the type, another example:

Typeclasses: Polymorphism in Haskell, Ord; Show; Read; Enum; Bounded; Deriving Typeclasses However, while sometimes we can operate on any type variable a , sometimes we'd like to have In some languages, all types will support the equality operator. toEnum :: Int -​> a fromEnum :: a -> Int -- Functions that the list syntax desugars to. As Ben pointed out, a context bound represents a "has-a" constraint between a type parameter and a type class. Put another way, it represents a constraint that an implicit value of a particular type class exists. When utilizing a context bound, one often needs to surface that implicit value.

Chapter 6. Using Typeclasses, Typeclasses are among the most powerful features in Haskell. file: ch06/​eqclasses.hs class BasicEq a where isEqual :: a -> a -> Bool those of you coming from an object-oriented background, as we are not really defining the same thing. You probably know that operators such as + can work with just about all of these. The context specifies that the type variable a must be a member of the Eq typeclass in order to implement the Ord typeclass for that variable. In this case, Eq a is required for Ord a because it is nonsensical to have an ordering unless we have equality, since clearly compare can be used to implement (==) .

What are Scala context bounds? | FAQ, Context bounds were introduced in Scala 2.8.0, and are typically used with the so​-called type class pattern, a pattern of code that emulates the functionality A context bound requires a parameterized type, such as Ordered[A] , but unlike String . Actually, the syntax I showed are syntactic sugars for what really happens. The Enum typeclass is for things that have predecessors and successors. We can also make it part of the Bounded typeclass, which is for things that have a lowest possible value and highest possible value. And while we're at it, let's also make it an instance of all the other derivable typeclasses and see what we can do with it.

Proceedings of the 1992 ACM Conference on LISP and Functional , Parametric type classes are a conservative extension of Haskell ' s type system it is ambiguous : Type variable a occurs in the context ( Sequence a s ) , but not in the translation rules for list comprehensions ; no special syntax is needed . Also related are Kaes ' work on parametric overloading ( Kae88 ) , F - bounded  A typeclass is a sort of interface that defines some behavior. If a type is a part of a typeclass, that means that it supports and implements the behavior the typeclass describes. A lot of people coming from OOP get confused by typeclasses because they think they are like classes in object oriented languages.

Comments
  • I'm not sure I understand the conflict you are trying to set up with this question. For example, you ask why this has the same syntax as the "has type" operator... but it is the "has type" operator! Similarly, you say normally :: is like a type annotation; and then you show an example where :: is used as a type annotation (and imply that this is not the normal use... but it is the normal use).
  • Does it help to know that :: is syntax, not an operator? "Operator" in Haskell has a specific meaning - an infix function that can be sectioned. There are several tokens made from operator characters that are reserved as syntax instead, like <-, ->, |, and ::. (That list is not exhaustive.) They aren't operators, and they don't work like operators. They're just syntax.
  • I wish more Haskell answers were like this, thorough and detailed.
  • "another example:" ... ? something's missing. :)