`type = Typeclass` compiles, but `type = (Typeclass, Typeclass)` doesn’t

I’ve been reading about the renaming of pre-existing types using the haskell’s type and, as much as I could understand, the type is only used for renaming concrete types, like Int, Integer, Char, Double, etc. So it is not used for renaming typeclasses, like Eq, Integral, Ord, etc. (Again, this is just as much, as I could understand, I don’t have 100% certainty if I am correct)

But here is the thing: I tested renaming Num and it worked!

type NumRenamed = Num

sillyNewFun :: NumRenamed a => a
sillyNewFun = 2

sillyNewFun2 :: NumRenamed a => a
sillyNewFun2 = 3

and it compiles fine, but when I check the type of :t (sillyNewFun + sillyNewFun2), for example, it returns me (sillyNewFun + sillyNewFun2) :: Num a => a. Notice that, although it worked, internally, GHC/GHCi still treats NumRenamed like Num (at least that’s my guess, let me know if I am right, though).

But if we have tried doing something like:

type NumRenamed = (Num,Num)

sillyNewFun :: NumRenamed
sillyNewFun = (2,3)

We would have gotten a rather long error message, while if we had tried doing the exact same thing with a concrete type, nothing would have exploded. This is the error message I am talking about:

teste5.hs:1:20: error:
    • Expecting one more argument to ‘Num’
      Expected a type, but ‘Num’ has kind ‘* -> Constraint’
    • In the type ‘(Num, Num)’
      In the type declaration for ‘NumRenamed’
  |
1 | type NumRenamed = (Num,Num)
  |                    ^^^

teste5.hs:1:24: error:
    • Expecting one more argument to ‘Num’
      Expected a type, but ‘Num’ has kind ‘* -> Constraint’
    • In the type ‘(Num, Num)’
      In the type declaration for ‘NumRenamed’
  |
1 | type NumRenamed = (Num,Num)
  |                        ^^^
Failed, no modules loaded.

So why this happens ? I thought we couldn’t rename typeclasses, but we kinda can as shown on the first code example, but when we try to push further and make a tuple of Num’s we can’t. So, my question is: Why the first example worked and the last one didn’t and, if there is a way to fix it, how can we do it ?

Sidenote:
If we wanted to compare the renaming of the typeclass of the first example with the renaming of a concrete type, we could make:

type NumRenamed = Int

sillyNewFun :: NumRenamed
sillyNewFun = 2

sillyNewFun2 :: NumRenamed
sillyNewFun2 = 3

and then :t (sillyNewFun + sillyNewFun2) returns (sillyNewFun + sillyNewFun2) :: NumRenamed. Notice that when we used a concrete type, internally GHC/GHCi recognizes the new type, so it actually says that this expression is of type NumRenamed, not Int, differently from the previous example when we redifined the Num, where it said the expression was still a Num.

1

Let’s examine the kind of Num in ghci:

ghci> :set -XNoStarIsType
ghci> :kind Num
Num :: Type -> Constraint

(NoStarIsType is to use the kind Type instead of the kind * for printing, which IMHO is clearer.)

So Num can be understood as a type-level “Constraint constructor” that takes a type like Int and returns a Constraint like Num Int.

NumRenamed has the same kind. You could also have been more explicit with the type argument, like:

ghci> type NumRenamed a = Num a
ghci> :kind NumRenamed
NumRenamed :: Type -> Constraint

But here

type NumRenamed = (Num,Num)

You are not defining something of kind Type -> Constraint. The Nums in the tuple of constraints are not applied to some type argument. You end up trying to define a weird type-level tuple of unapplied “Constraint constructors” that isn’t something useable as a function constraint.

Instead, you could be more explicit with the type argument:

ghci> type NumRenamed a = (Num a, Num a)
ghci> :kind NumRenamed
NumRenamed :: Type -> Constraint

By the way, if for some reason you really wanted the weird type-level tuple, you could do it like this:

ghci> :set -XDataKinds
ghci> type NumRenamed' = '(Num, Num)
ghci> :kind NumRenamed'
NumRenamed' :: (Type -> Constraint, Type -> Constraint)

Term-level expressions have types, e.g. True :: Bool, const True :: Int -> Bool.

Type-level expressions have kinds, e.g. Bool :: Type, Maybe :: Type -> Type.

It was not so complex in the past, but Haskell evolved quite a lot. In GHCi you can ask for a type with :t, and for a kind with :k.

Now, type classes like Num are “type-level expressions”, and have a kind as well: Num :: Type -> Constraint. We also have Num Int :: Constraint.

Expressions of kind Constraint is what we write in types, e.g.

foo :: Num a => a -> a
foo x = x+1

uses a :: Type, Num a :: Constraint, a -> a :: Type.

The syntax type T = ... can now bind type level expressions.

import Data.Kind

type N :: Type -> Constraint
type N = Num

We also have more complex (and confusing) uses:

-- pair of type classes
type P :: (Type -> Constraint, Type -> Constraint)
type P = '(Num, Show)

-- pair of constraints    
type Q :: (Constraint, Constraint)
type Q = '(Num Int, Show Bool)

-- This is the "AND" of two constraints, not a pair!
type R :: Type -> Constraint
type R a = (Num a, Show a)   -- note: no ' here!

(By the way, the syntax (..,..) is vastly overloaded in Haskell, leading to great confusion at first. Then we also have '(..,..) to make things worse. I wish we used distinct syntax for each case.)

Note that type ... does not really define new types or type classes, but it is only an alias. In particular, the last example above does not allow to use R without applying to an argument. We get an error otherwise:

type S :: (Type -> Constraint, Type -> Constraint)
type S = '(Num, R)

* The type synonym `R' should have 1 argument, but has been given none
* In the type synonym declaration for `S'

This is a bit counterintuitive. Just remember that aliases type ... do not allow you to do anything you can not do without, by replacing their definition at each use point.

After this

type NumRenamed = (Num,Num)

NumRenamed is simply a name for the type expression (Num,Num) and will in most contexts behave exactly the same as if you had written (Num,Num) instead.

So this fails:

sillyNewFun :: NumRenamed
sillyNewFun = (2,3)

for exactly the same reason that this fails:

sillyNewFun :: (Num,Num)
sillyNewFun = (2,3)

The problem has nothing to do with not being able to use type to declare a new name for a type class, the problem is simply that you can’t use type class names the way you were trying to use them. (Num,Num) is not a valid way to write the type of a pair of numbers, so using a type alias for (Num,Num) doesn’t work either.

Take another look at your first example:

type NumRenamed = Num

sillyNewFun :: NumRenamed a => a
sillyNewFun = 2

sillyNewFun2 :: NumRenamed a => a
sillyNewFun2 = 3

Here it seems you understood that a type class like Num (referenced by the alias NumRenamed) needs to be used in a constraint, to the left of the => arrow. sillyNeeFun2 is not given the type Num or NumRenamed, but NumRenamed a => a. Translated to English this says “sillyNewFun2 can be used as any type a that is a member of the type class NumRenamed” (and of course NumRenamed is another name you have assigned to the type class Num, so that means any type that is a member of Num).

But then with your type NumRenamed = (Num, Num) example you appear to have forgotten that. Now sillyNewFun :: NumRenamed translates into English as “sillyNewFun is a NumRenamed“, but NumRenamed is a name for (Num, Num), which isn’t using the Num class to constrain the set of types a variable ranges over, it’s instead a pair of (unapplied!) type class constraints; it doesn’t really make any sense.

The syntax you’re looking for would be:

pairSameType :: Num a => (a, a)
pairSameType = (2, 3)

or possibly:

pairPossiblyDifferntTypes :: (Num a, Num b) => (a, b)
pairPossiblyDifferntTypes = (2, 3)

And note that these two are quite different, which is one reason why we use a more verbose syntax with type variables; if we just used (Num, Num) to mean “the type of a pair of values that are both of types in the Num class” there would be no easy way to distinguish these different use cases.

You can then use type aliases to rename some parts of those type expressions if you wish, although if you want to bury constraints like Num a under a type alias the rules are a little more complicated than simply imagining that the RHS of the alias is directly replacing everywhere you write the alias name.

1

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật