TypeSynonymInstances allow only full synonyms. With
{-# LANGUAGE TypeSynonymInstances #-}
type RTuple b a = (a, b)
instance Monad (RTuple b) where ...
-- or
instance Monad (RTuple b :: * -> *) where ...
I got next error:
test.hs:17:22:
Type synonym `RevTuple' should have 2 arguments, but has been given 1
In the instance declaration for `Monad (RTuple b :: * -> *)'
Trac field | Value |
---|---|
Version | 7.6.3 |
Type | Bug |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |
Wow! Great book! Thank you very much!
I have Windows without VM. And even compile GHC from 0 (even without any contribution) will be a big challenge )) So, maybe this is not an option right now.
I'm not familiar with low-Haskell code, but yes, it is easy!
We can divide changes to support code and significant code.
This is a dev's routine, I a newbie with these techniques. It is needed to
add extension name to extension
add parsing extension pragma
add line "use -XRankedInstances or -XOverlappingInstances" in error message if both RankedInstances and OverlappingInstances are off
I recommend deny to use both RankedInstances and OverlappingInstances in one module
add data Ranked-Instances, absolutely similar as data Overlapping-Instances
add parsing pattern to parser/Parser.y.pp, something like | "instance" "rank" digits "." <rest>
. But forbid to parse without extension
extract number
and add "rank" field to all data Instance<Smth> and copy-paste this field everywhere it is needed.
Fortunately, almost all code is already written by guys, who added Overlapping instances to GHC!
The reason - Ranked Instances are "user control overlapping".
http://ghc.haskell.org/trac/ghc/browser/ghc/compiler/types/InstEnv.lhs\#L564
lookupInstEnv ... =
pruned_matches = foldr insert_overlapping [] all_matches
(safe_matches, safe_fail) = if length pruned_matches == 1
then check_safe (head pruned_matches) all_matches
else (pruned_matches, False)
We need to change 2 functions - insert_overlapping
and check_safe
.
** insert_overlapping **
Let's look to insert_overlapping
: http://ghc.haskell.org/trac/ghc/browser/ghc/compiler/types/InstEnv.lhs\#L618
This function is fully satisfied, exept for beats
sub-function.
http://ghc.haskell.org/trac/ghc/browser/ghc/compiler/types/InstEnv.lhs\#L641
(instA, _) `beats` (instB, _)
= overlap_ok &&
isJust (tcMatchTys (mkVarSet (is_tvs instB)) (is_tys instB) (is_tys instA))
-- A beats B if A is more specific than B,
-- (ie. if B can be instantiated to match A)
-- and overlap is permitted
We cahange it to
(instA, _) `beats` (instB, _)
= (fromRank instA < fromRank instB) ||
{- We don't need to check if extension is on,
all non-extension instances have a privilege - the lowest 0 rank -}
overlap_ok && ...
We add (!) just 1 line of significant code.
** check_safe **
This a bit complicated. Devs, who add Overlapping instances add a guard : "We restrict code compiled in 'Safe' mode from overriding code compiled in any other mode."
But we wish to allow over-rank instances through the modules. The simplest way - to forbid use Overlapped-Instances together with Ranked-Instances in one module. And add such lines from: http://ghc.haskell.org/trac/ghc/browser/ghc/compiler/types/InstEnv.lhs\#L609
inSameMod b =
let na = getName $ getName inst
la = isInternalName na
nb = getName $ getName b
lb = isInternalName nb
in (la && lb) || (nameModule na == nameModule nb)
into (this is not effective code, looks a bit hacky, but it works):
inSameMod b =
let na = getName $ getName inst
la' = isInternalName na
la = isRankedExtension || la' -- allowing Rank
nb = getName $ getName b
lb' = isInternalName nb
lb = isRankedExtension || lb' -- allowing Rank
in (la && lb) || (nameModule na == nameModule nb)
Yes, Ranked Instances is very powerful extension! And yes, it is easy to implement!
Yes, I'm confused, no one even reply at my proposal. I don't even speak about serious discussions.
And yes, I regret, I have no one advocate through developers, who could add this extension less than a day in a test branch.
I've showed, how to add 3 lines of significant code (sure, it is needed some routine code for support this extension.)
I hope, it's become clearer.
Am I missing something? Is it still overly complex in implementation?
I'm really deeply appreciated !
And I'm really sorry if my examples are not understandable and my explanation isn't clearly enough ((
Here is a background: the main power of Haskell is type-classes and their implementation: instances. So, Haskell is interested to have as much instances as it can for any cases.
Unfortunately we can't have a lot of instances - they became to overlap each other. In many cases Overlapping Instances doesn't solve our problems. GHC add "default" instances. But it is a partially solved problem.
For now we are capable to write next:
instance Monad m => Functor m where
fmap = liftM
instance Applicative m => Functor m where
fmap f x = pure f <*> x
But we cannot add them of overlapping reason. Both of them not only overlap with any concrete instance but each other. "Defaulting" and overlapping won't help us to solve this. All (except hidden with "defaulting") superclass' instances like:
instance Child a => Parent a where ..
are forbidden
Ranked Instances allow us to create and use all Child a => Parent a
superclass' instances
class Applicative f where ... --without "Functor f=>", it is a misfeature
instance rank 15. Monad m => Functor m where
fmap = liftM
instance rank 16. Applicative m => Functor m where
fmap f x = pure f <*> x
Compiler try use all 0-ranked instances, then all 1-ranked, then ..., then all 14-ranked, then all 15-ranked.
If instances overlap in any rank-layer, it is an error. Our 2 instances are in different layers, so we have no overlapping.
All user's instances by default are 0-ranked. So if user create data, add 2 instances: Monad and Applicative,
data D a ...
instance Monad D where ...
class Applicative f where ... --without "Functor f=>"
instance Applicative D where ...
and not Functor, but try to use fmap
foo = (+) `fmap` D 1
compiler use (Monad m => Functor m) instance, reason - lower rank, than (Applicative m => Functor m). If user define his own instance Functor data,
instance {- ranked 0. -} Functor D where ..
it will be 0-ranked instance, so compiler use his instance, not superclass' instance (with much higher rank).
''A lot of Child2Parent superclass' instances are the unique feature of this extension! ''
Ranked Instances allow us to get rid of default instances, but save all capabilities of it. Even more - with Ranked Instances is allowed to use several default-like instances (outside of class)
class ToJSON a where
toJSON :: a -> Value
default instance (Generic a, GToJSON (Rep a)) => ToJSON a where
toJSON = genericToJSON defaultOptions
is the same as
class ToJSON a where
toJSON :: a -> Value
instance rank 10. (Generic a, GToJSON (Rep a)) => ToJSON a where
toJSON = genericToJSON defaultOptions
instance rank 11. (Data a) => ToJSON a where ...
Here we have 2 default-like instances! If we wish to add third, I dunno, "Typeable a=> ToJson a" we still could add it (with higher rank).
''We'll have with Ranked Instances much more, then with default instances! ''
Ranked Instances are powerful enough to use them instead of both Overlapping Instances and Incoherent Instances.
instance rank 3. C a Int Bool where ...
instance rank 2. C Int Int a where ...
instance rank 1. C a Bool a where ...
instance rank 0. C Bool a Bool where ...
Neither Overlapping Instances, nor Incoherent Instances can't help us in this situation.
With Ranked Instances, Compiler try to
- 0-rank: look if first and third args are Bool
- if no, 1-rank: look if second arg is Bool
- if no, 2-rank: look if first and second args are Int
- if no, 3-rank: look if second argument is Int and third is Bool
- if no, throw an error
''We'll have with Ranked Instances much more, then Overlapping Instances and Incoherent Instances together! ''
If we summarize, Ranked Instances is a very powerful extension!
It is easy to implement, easy to use and easy to read the code.
It is compatible with all old code and other extensions.
It is a step forward, not aside.
It allows to write more generic code.
It helps to get rid of boilerplate.
I suppose, for now my proposal looks much more clearly. If it is something unclear, I give more explanation.
This is a first part of 3 depended extensions: Ranked Instances => Inherit Instances => Newclasses
This is very easy to implement. And it gives big advantages. And further developing. I suggest to add "rank" to instance.
Now the system of instances is flat, and compiler takes all of instances and check if only 1 match. If it is not(less or more than 1) - compiler trow an error.
With rankings compiler
We use RankedInstances
before OverlappingInstances
(which must include RankedInstances
), and in many cases instead of.
We add "rank N" between "instance" word and ".", where N is a number
{-# LANGUAGE RankedInstances #-}
instance rank 1. C a => D a where ...
If instance has no rank, this means it has 0 rank
instance C a => D a where ...
~
instance rank 0. C a => D a where ...
This is a backward compatibility.
Examples
instance rank 1. C Int a where ... -- (A)
instance C a Bool where ... -- (B) rank 0
1+)
instance rank 1. C Int a where ... -- (A)
instance rank 1. C a Bool where ... -- (B)
instance C Int Bool where ... -- (C) rank 0
instance rank 1. C Int [a] where ... -- (C)
instance C Int [Int] where ... -- (D)
As we see, all instances are unambiguous!
Without -XRankedInstances all instances with hight rank n, where n >0 - are not exported
More information (about all 3 extensions) is here http://haskell.1045720.n5.nabble.com/Proposal-RankedInstances-tt5737152.html
Trac field | Value |
---|---|
Version | 7.6.3 |
Type | FeatureRequest |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |
Haskell community has a long discussion how to implement a superclasses into Haskell.
Now it is used default
method. But it looks ugly!
But all their abilities are already implemented!
We need just 2 extensions: FlexibleInstances and UndecidableInstances
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
instance Monad m => Applicative m where
pure = return
(<*>) = ap
instance Monad m => Functor m where
fmap = liftM
instance Monad m => Bind m where
(>>-) = flip (>>=)
B.join = M.join
this code is valid!
I've already defined 3 "superclassses" for Monad: Functor, Applicative and Bind!
"superclass' instances" have unique quality from Programming Patterns and typeclasses ideology: do not inherit, extend!
We could easily extend "superclasses" and make a lot of class' dependences.
We don't need to insert inside the class some ugliness like
default return :: Applicative f => a -> f a
return = pure
Next is much prettier!
instance Monoid m => Alternative m where
(<|>) = mplus
empty = mzero
We could even use it with Generic
without any default
:
class ToJSON a where
toJSON :: a -> Value
instance (Generic a, GToJSON (Rep a)) => ToJSON a where
toJSON = genericToJSON defaultOptions
So, I suggest to made true "Applicative and Functor are superclasses of Monad" and add all necessary superclass' instances to base libraries.
Trac field | Value |
---|---|
Version | 7.6.3 |
Type | FeatureRequest |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | libraries/base |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |
May be I misunderstood, it looks like this case is a part of 'infinite type':
> let u x = (x,u)
<interactive>:33:14:
Occurs check: cannot construct the infinite type:
t1 = t0 -> (t0, t1)
In the expression: u
In the expression: (x, u)
In an equation for `u': u x = (x, u)
Mega-polymorphic type! This looks like a bug, but may be it is a feature
u = u
b = u 2 + u "extra" "poly" "arguments" True
b' = u ++ u True
eq' = (u :: Bool) == (u 3.14 :: Bool)
eq'' = (u 1 :: Num t => t) == (u "strings" "a lot" :: Num z => z)
eq = u == u 2 -- ghci only
> :t u
u :: t
This code is valid for TypeChecker
Trac field | Value |
---|---|
Version | 7.6.3 |
Type | Bug |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler (Type checker) |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |
Trac field | Value |
---|---|
Resolution | Unresolved → ResolvedInvalid |
Trac field | Value |
---|---|
Resolution | Unresolved → ResolvedInvalid |
Let's allow to use kinds as "I don't care"/"it doesn't matter" type variables at function's signature
fst :: (a, *) -> a
This is the same '_' - "i don't care" variable, but in signatures.
fst (a, _ ) = a
And "_" remains as "black hole" at signatures
Trac field | Value |
---|---|
Version | 7.6.3 |
Type | FeatureRequest |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |
Let's allow to use kinds as "I don't care"/"it doesn't matter" type variables at function's signature
fst :: (a, *) -> a
This is the same '_' - "i don't care" variable, but in signatures.
fst (a, _ ) = a
And "_" remains as "black hole" at signatures
Trac field | Value |
---|---|
Version | 7.6.3 |
Type | FeatureRequest |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |
Let's allow to use kinds as "I don't care"/"it doesn't matter" type variables at function's signature
fst :: (a, *) -> a
This is the same '_' - "i don't care" variable, but in signatures.
fst (a, _ ) = a
And "_" remains as "black hole" at signatures
Trac field | Value |
---|---|
Version | 7.6.3 |
Type | FeatureRequest |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |
Let's allow to use kinds as "I don't care"/"it doesn't matter" type variables at function's signature
fst :: (a, *) -> a
This is the same '_' - "i don't care" variable, but in signatures.
fst (a, _ ) = a
And "_" remains as "black hole" at signatures
Trac field | Value |
---|---|
Version | 7.6.3 |
Type | FeatureRequest |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |
For
data Foo (a::k) = Foo a
we have next right error:
Kind mis-match
Expected kind `OpenKind', but `a' has kind `k'
In the type `a'
In the definition of data constructor `Foo'
In the data declaration for `Foo'
but for same GADTs data we have just
data Foo (a::k) where
Foo a :: Foo a
<interactive>:38:27: parse error on input `a'
Trac field | Value |
---|---|
Version | 7.6.3 |
Type | Bug |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |
True :: Bool :: * :: ** :: *** :: **** :: ...
This is nice!
Any rankNkinds is significant to work with rank(N-1)kinds data and classes only!
'a' is exists anyway - this is exactly type.
In class Typeable3 (a :: ***) where ...
'm' isn't significant at all. Is significant to type any rank2kinds data.
But is significant in
foo :: (a :: (k :: (m :: ***))) -> (a :: (k :: (m :: ***))) -> b
-- same first and second args
class Foo (a :: (k :: (m :: ***))) where
data Bar :: (m :: ***)
data Baz :: (k :: **)
...
Let we have
data HomoCategory a :: (k :: **) -> a :: (k :: **) -> * where ...
data HeteroCategory (a :: **) -> (b :: **) -> * where ...
class AnyCategory (a :: ***) where ...
instance AnyCategory HomoCategory where ...
instance AnyCategory HeteroCategory where ...
Nothing extraordinary is in the right, just Haskell:
class AnyCategory (a :: ***) where
id :: (a :: ***) -> (a :: ***)
id a = a
Additional propositions:
(I) We already have a little hell with parentheses. We have
infixr 9 (::)
I suggest to have
infixr 0 (::`)
And we could easily write
let s = "Hello" ::` String ++ " world" ::` [Char] :: String
let i ::` Int = 2
class Foo a ::` k ::` * b ::` * where ...
(II) For rankNkinds will be useful
if we have
foo :: (a :: (k :: *)) -> (a :: (k :: *)) -> b
is Ok.
But next is not:
class Typeable (a :: (k :: *)) where ... (without k) ...
Solution is to have "many stars":
data Foo ** -> ** -> * where ...
class Typeable (a :: **) where ...
class TypeableOmega (a :: *******) where ...
Agda Haskell
Set0 a::*
Set1 a::k <<==>> a :: (k :: *)
Set2 a :: (k2 :: (k :: *))
...
We already have a lot of rank2kinds(PolyKinds), so we already need to have rank3kinds.
Developers said they did new Typeable in GHC 7.8, but it is impossible to write
data Foo (a::(b::k)->*) deriving Typeable
new Typeable = Typeable2kinds; but Foo is a part of Typeable3kinds
MetaKinds could be name as RankNKinds
MetaKinds is more general then PolyKinds.
Now we could write with PolyKinds
class Foo (a :: k) where ...
It would be nice if we could also write with MetaKinds next:
class Foo (a :: (k :: *)) where ...
As we already have Rank2Types and RankNTypes,
PolyKinds ~ Rank2Types,
MetaKinds ~ RankNTypes
This will be valid:
class Foo (a :: (k :: (u :: *))) where ...
Trac field | Value |
---|---|
Version | 7.6.3 |
Type | FeatureRequest |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |