TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread oleg

Dan Doel wrote:
>class C a b | a -> b where
>  foo :: a -> b
>  foo = error "Yo dawg."
>
>instance C a b where

The instance 'C a b' blatantly violates functional dependency and
should not have been accepted. The fact that it was is a known bug in
GHC. The bug keeps getting mentioned on Haskell mailing lists
about every year. Alas, it is still not fixed. Here is one of the
earlier messages about it:

  http://www.haskell.org/pipermail/haskell-cafe/2007-March/023916.html

HList does NOT depend on that invalid behavior. The bug is relatively
recent (introduced around 2006); HList worked in 2004.




___
Haskell-prime mailing list
Haskell-prime@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-prime


Re: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread oleg

I'd like to summarize the relationship between functional dependencies
and type functions, and propose a solution that should get rid of
overlapping instances. The solution does not require messing with
System-FC. In fact, it can be implemented today (although
ungainly). A small bit of GHC support will be appreciated.

I believe that functional dependencies and type functions are not
comparable. There are examples that can't be done (in principle or in
practice) with type functions, and there are examples that cannot be
implemented with functional dependencies.

The example of the latter is
http://okmij.org/ftp/Computation/extra-polymorphism.html#injectivity

Functional dependencies have the advantage in the following two cases

 1. mutual dependencies:
class Add x y z | x y -> z, x z -> y, y z -> x

I think this example can be emulated with type functions; the
emulation didn't work with GHC 6.10, at least. It may work now.

 2. combination with overlapping instances

The type equality predicate is only one example, there are several of
that kind. Just for the record, the following code

class Eq a b c | a b -> c
instance Eq k k True
instance Eq j k False

never worked. (GHC may have accepted the above declarations; that
doesn't mean much since overlapping was detected lazily). The
key insight of HList is the realization why the code above did not
work and how to hack around it (TypeCast or ~ is needed).

If GHC somehow provided Eq as a built-in, that would not have been
enough. We need something like Eq at higher kinds. For example,

http://okmij.org/ftp/Haskell/typecast.html#is-function-type

That web page gives example of IsList, IsArray and other such
predicates.

The most practical application of overlapping instances is documented
in

http://www.haskell.org/pipermail/haskell-cafe/2010-June/078590.html
http://www.haskell.org/pipermail/haskell-cafe/2010-June/078739.html


The solution has been described already in the HList paper: provide
something the typeOf at the type level. That is, assume a type
function

TypeOf :: * -> TYPEREP

where TYPEREP is a family of types that describe the type of TypeOf's
argument in the way TypeRep value described the type. The HList paper
(Sec 9) has outlined an ugly solution: associate with each type constructor a
type-level numeral; TYPEREP is a type-level list then, which contains 
the code for the head constructor and TYPEREPs of the arguments.
TYPEREPs are easily comparable.

The HList solution is certainly ugly and non-scalable. The biggest
problem is assigning opaque integers to type constructors and ensuring
the uniqueness. Once I toyed with the idea of defining a family of
types to be the HList of Dot and Dash, so we would spell the name
of the constructor in Morse code.

I must stress that my proposal is rather timid: Matthias Blume, 
when designing a new FFI for SML/NJ has lifted the whole latin alphabet
to the type level:
http://people.cs.uchicago.edu/~blume/papers/nlffi-entcs.pdf
so he could spell the names of C struct in SML types. This NLFFI has
been used in SML/NJ, in production, for about 10 years. If SML
developers could do that, certainly GHC developers could.


Persistent rumors hold that someone is working on adding more kinds to
GHC. A Kind NAME and a Kind TYPEREP seem good kinds to have (along
with naturals, of course). GHC could then provide the type function
TypeOf. Once we have a TYPEREP, we can compare and deconstruct it,
removing the need for overlapping instances.


___
Haskell-prime mailing list
Haskell-prime@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-prime


Re: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread dm-list-haskell-prime
At Tue, 14 Jun 2011 19:21:38 -0400,
Dan Doel wrote:
> 
> If this is really about rules for selecting instances unambiguously
> without any regard to whether some parameters are actually functions
> of other parameters, then the name "functional dependencies" is pretty
> poor.

Maybe "functional dependencies" is a poor name.  I'm not going to
argue it's a good one, just that the extension is quite useful.
Despite what the name suggests, it is most useful to think of
"| a b -> c d" as meaning "any method that uses types a and b does not
need to use types c and d for the compiler to determine a unique
instance".  Once you start thinking of fundeps that way (and always
keep in mind that contexts play no role in instance selection), then I
think it gets a bit easier to reason about fundeps.

> I know the actual implementation is, too. But it's because of the
> limited way in which fundeps are used. If I'm not mistaken, if they
> were modified to interact with local constraints more like type
> families (that is, correctly :)), then soundness would be a concern
> with these examples.

I don't think so.  Because fundeps (despite the name) are mostly about
instance selection, incoherent fundeps won't violate safety.  Your
program may incoherently chose between methods in multiple instances
that should be the same instance, but at least each individual method
is type safe.  With type families, you can actually undermine type
safety, and there's no cheating way to fix this, which is why I think
TypeFamilies could by very dangerous when combined with dynamic
loading.

> I understand why the instance resolution causes these to be different
> in Haskell. I think the first one is a bad definition by the same
> criteria (although it is in practice correct due to the constraint),
> but UndecidableInstances turns off the check that would invalidate it.

Though I realize you are unlikely ever to like lifting the coverage
condition, let me at least leave you with a better example of why
Undecidable instances are useful.  Suppose you want to define an
instance of MonadState for another monad like MaybeT.  You would need
to write code like this:

instance (MonadState s m) => MonadState s (MaybeT m) where
get = lift get
put = lift . put

This code fails the coverage condition, because class argument
(MaybeT m) does not contain the type variable s.  Yet, unlike the
compiler, we humans can look at the type context when reasoning about
instance selection.  We know that even though our get method is
returning an s--meaning really "forall s. s", since s is mentioned
nowhere else--there is really only one s that will satisfy the
MonadState s m requirement in the context.  Perhaps more importantly,
we know that in any event, if the code compiles, the s we get is the
one that the surrounding code (calling get or put) expects.

Now if, in addition to lifting the coverage condition, you add
OverlappingInstances, you can do something even better--you can write
one single recursive definition of MonadState that works for all
MonadTrans types (along with a base case for StateT).  This is far
preferable to the N^2 boilerplate functions currently required by N
monad transformers:

instance (Monad m) => MonadState s (StateT s m) where
get = StateT $ \s -> return (s, s)

instance (Monad (t m), MonadTrans t, MonadState s m) =>
MonadState s (t m) where
get = lift get
put = lift . put

This is not something you can do with type families.  So while
UndecidableInstances and OverlappingInstances aren't very elegant, the
fact that they can reduce source code size from O(N^2) to O(N) is a
good indication that there is some unmet need in the base language.

David

___
Haskell-prime mailing list
Haskell-prime@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-prime


Re: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread Dan Doel
On Tue, Jun 14, 2011 at 5:38 PM,
 wrote:
> Undecidable instances are orthogonal to both FunctionalDependencies
> and OverlappingInstances.  They concern whether or not the compiler
> can guarantee that the typechecker will terminate.  You can have
> undecidable instances without either of the other two extensions

I'm aware of what UndecidableInstances does.

But in this case, it's actually ensuring the soundness of the
computational aspect of functional dependencies, at least in the case
where said computation were fixed to incorporate the things type
families can do. Which would be useful, because fundeps don't interact
with GADTs and such correctly.

> The coverage condition is part of a pair of pair of sufficient (but
> not necessary) conditions that GHC applies to know that typechecking
> is decidable.  It just says that if you have a dependency "a -> b",
> and you have type variables in b, then they should mention all the
> type variables in a.  Thus, the following code is legal without
> UndecidableInstances:
>
>        {-# LANGUAGE MultiParamTypeClasses #-}
>        {-# LANGUAGE FlexibleInstances #-}
>        {-# LANGUAGE FunctionalDependencies #-}
>
>        class A a b | a -> b
>        instance A [a] (Either String a)

In this instance, the second argument can be given as a function of the first:

f [a] = Either String a

> But the following program is not:
>
>        class A a b | a -> b
>        instance A a (Either String b)

In this instance, it cannot.

> No, that's not right.  Even with UndecidableInstances, you cannot
> define conflicting instances for functional dependencies.  Moreover,
> just because the GHC's particular typechecker heuristics don't
> guarantee the above code is decidable doesn't mean it isn't decidable.

You certainly can define conflicting instances if 'a -> b' is supposed
to denote a functional dependency between a and b, and not just a
clever set of rules for selecting instances.

> I think you are thinking of UndecidableInstances in the wrong way.
> For instance, the following code does not require
> UndecidableInstances, but has polymorphic type variables on the
> right-hand side of a functional dependency, which seems to be what you
> object to:
>
>        {-# LANGUAGE MultiParamTypeClasses #-}
>        {-# LANGUAGE FunctionalDependencies #-}
>
>        class C a b | a -> b where
>            foo :: a -> b
>
>        instance C (Either a b) (Maybe b) where
>            foo _ = Nothing
>
>        bar :: Maybe Int
>        bar = foo (Left "x")
>
>        baz :: Maybe Char
>        baz = foo (Left "x")

This is another case where the second argument is a function of the first:

f (Either a b) = Maybe b

bar uses the induced instance C (Either String Int) (Maybe Int), and
baz uses the induced instance C (Either String Char) (Maybe Char).

> Remember, FunctionalDependencies are all about determining which
> instance you select.  The functional dependency "class C a b | a -> b"
> says that for a given type a, you can decide which instance of C
> (i.e., which particular function foo) to invoke without regard to the
> type b.

Why are they called "functional dependencies" with "a -> b" presumably
being read "b functionally depends on a" if there is no actual
requirement for b to be a function of a?

> It specifically does *not* say whether or not type b has to
> be grounded, or whether it may include free type variables, including
> just being a type variable if you enable FlexibleInstances:

I don't personally count free type metavariables as Haskell types.
They are artifacts of the inference algorithm. Int is a type, and
(forall a. a) is a type. But a metavariable 'b' is just standing in
for a type until we can figure out what actual Haskell type it should
be. Haskell muddies this distinction by only allowing prenex
quantification, and writing 'a -> b' instead of 'forall a b. a -> b',
but the distinction is actually important, if you ask me.

>        {-# LANGUAGE MultiParamTypeClasses #-}
>        {-# LANGUAGE FunctionalDependencies #-}
>        {-# LANGUAGE FlexibleInstances #-}
>
>        class C a b | a -> b where
>            foo :: a -> b
>
>        instance C (Either a b) b where  -- no UndecidableInstances needed
>            foo _ = undefined

This is yet another example where the second parameter is actually a
function of the first.

> Again, unique but not grounded.  Either Maybe c or "forall c" is a
> perfectly valid unique type, though it's not grounded.  People do
> weird type-level computations with fundeps using types that aren't
> forall c.  (The compiler will fail if you additionally have an
> instance with forall c.)

(forall c. Either Maybe c) is a valid type. "forall c" on its own is
not a type at all, it's a syntax error. And a metavariable 'c' is
something that exists during inference but not in the type system
proper.

> I think the reason you have the right-hand side is so you can
> say things like "| a -> b, b -> a".

The reason they h

RE: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread Simon Peyton-Jones
Some brief rejoinders.

1.  Golly, functional dependencies are tricky aren't they?  A big reason I like 
type families better is simply that I can understand them. 

2.  Thank you for correcting my equality example.  The code I gave 
>class Eq a b c | a b -> c
>instance Eq k k True
>instance Eq j k False
doesn't work, and has never worked. The form that does work is
>class Eq a b c | a b -> c
>instance Eq k k True
>instance (r~False) => Eq j k r

3. There have been some questions about soundness and fundeps.  Don't worry.  
I'm certain GHC's implementation fundeps is sound.  Fundeps in GHC are used 
*only* to add extra equality constraints that do some extra unifications.  (Yes 
this is a statement about the type inference algorithm, rather than about the 
type system.   I don't know how to give a satisfactory non-algorithmic 
treatment of fundeps.)  

GHC then translates the type-checked program into System FC, and (if you use 
-dcore-lint) is redundantly typechecked there (no fundeps involved).  So, no 
soundness worries: GHC may reject a program you want it to accept, but a 
program it accepts won't go wrong at runtime.   [Barring the notorious Trac 
#1496]

4. Dan asks why
instance (r~False) => Eq j k r
could possibly differ from 
instance Eq j k False
The reason is this.  You could imagine permitting this:
instance C a => C [a]
instance D a => C [a]
meaning "to satisfy C [a] try to satisfy C a. Failing that, you can instead try 
D a".  So constraint solving would need a search process.  But Haskell doesn't 
do that. It insists that instance "heads", C [a] in this case, are distinct.  
(Usually non-overlapping.)  So the instance head (Eq j k r) is different from 
(Eq j k False); the latter matches more often, but then requires (r~False).

Simon

|  -Original Message-
|  From: haskell-prime-boun...@haskell.org [mailto:haskell-prime-
|  boun...@haskell.org] On Behalf Of dm-list-haskell-pr...@scs.stanford.edu
|  Sent: 14 June 2011 22:39
|  To: Dan Doel
|  Cc: haskell-prime@haskell.org
|  Subject: Re: TypeFamilies vs. FunctionalDependencies & type-level recursion
|  
|  At Tue, 14 Jun 2011 15:09:02 -0400,
|  Dan Doel wrote:
|  >
|  > On Tue, Jun 14, 2011 at 1:19 PM,
|  >  wrote:
|  > > No, these are not equivalent.  The first one "TypeEq a b c" is just
|  > > declaring an instance that works "forall c".  The second is declaring
|  > > multiple instances, which, if there were class methods, could have
|  > > different code.  The second one is illegal, because given just the
|  > > first two types, a and b, you cannot tell which instance to pick.
|  >
|  > Then why am I not allowed to write:
|  >
|  > class C a b | a -> b
|  > instance C T [a]
|  >
|  > without undecidable instances? GHC actually complains in that case
|  > that the coverage condition is violated. But it is a single well
|  > specified instance that works for all a.
|  
|  Undecidable instances are orthogonal to both FunctionalDependencies
|  and OverlappingInstances.  They concern whether or not the compiler
|  can guarantee that the typechecker will terminate.  You can have
|  undecidable instances without either of the other two extensions, for
|  instance:
|  
|   {-# LANGUAGE FlexibleInstances #-}
|  
|   class A a
|   class B a
|   instance (A a) => B a   -- illegal w/o UndecidableInstances
|  
|  The coverage condition is part of a pair of pair of sufficient (but
|  not necessary) conditions that GHC applies to know that typechecking
|  is decidable.  It just says that if you have a dependency "a -> b",
|  and you have type variables in b, then they should mention all the
|  type variables in a.  Thus, the following code is legal without
|  UndecidableInstances:
|  
|   {-# LANGUAGE MultiParamTypeClasses #-}
|   {-# LANGUAGE FlexibleInstances #-}
|   {-# LANGUAGE FunctionalDependencies #-}
|  
|   class A a b | a -> b
|   instance A [a] (Either String a)
|  
|  But the following program is not:
|  
|   class A a b | a -> b
|   instance A a (Either String b)
|  
|  > The answer is that such an instance actually violates the functional
|  > dependency, but UndecidableInstances just turns off the checks to make
|  > sure that fundeps are actually functional. It's a, "trust me," switch
|  > in this case (not just a, "my types might loop," switch).
|  
|  No, that's not right.  Even with UndecidableInstances, you cannot
|  define conflicting instances for functional dependencies.  Moreover,
|  just because the GHC's particular typechecker heuristics don't
|  guarantee the above code is decidable doesn't mean it isn't decidable.
|  
|  > So I guess HList will still work fine, and UndecidableInstances are
|  > actually more evil than I'd previously thought (thanks for the
|  > correction, Andrea :)).
|  
|  I think you are thinking of UndecidableInstances in the wrong way.
|  For instance, the following 

RE: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread Simon Peyton-Jones
|   
http://hackage.haskell.org/trac/haskell-prime/wiki/FunctionalDependencies
|  
|  Currently under cons for FunctionalDependencies, it says:
|  
|   AssociatedTypes seem to be more promising.
|  
|  I proposed the following fix:
|  
|   AssociatedTypes seem to be more promising, but in their
|   current form are not as general as FunctionalDependencies
|   [link].

OK, that one.  I've made that change.

|  Is there a policy that only a proposal's "owner" can modify the wiki
|  page?  Or that you have to be a member of the Haskell' committee?  

I'm not sure.  Malcolm Wallace is chair at the moment; I'm ccing him.

Simon

___
Haskell-prime mailing list
Haskell-prime@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-prime


Re: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread dm-list-haskell-prime
At Tue, 14 Jun 2011 15:09:02 -0400,
Dan Doel wrote:
> 
> On Tue, Jun 14, 2011 at 1:19 PM,
>  wrote:
> > No, these are not equivalent.  The first one "TypeEq a b c" is just
> > declaring an instance that works "forall c".  The second is declaring
> > multiple instances, which, if there were class methods, could have
> > different code.  The second one is illegal, because given just the
> > first two types, a and b, you cannot tell which instance to pick.
> 
> Then why am I not allowed to write:
> 
> class C a b | a -> b
> instance C T [a]
> 
> without undecidable instances? GHC actually complains in that case
> that the coverage condition is violated. But it is a single well
> specified instance that works for all a.

Undecidable instances are orthogonal to both FunctionalDependencies
and OverlappingInstances.  They concern whether or not the compiler
can guarantee that the typechecker will terminate.  You can have
undecidable instances without either of the other two extensions, for
instance:

{-# LANGUAGE FlexibleInstances #-}

class A a
class B a
instance (A a) => B a   -- illegal w/o UndecidableInstances

The coverage condition is part of a pair of pair of sufficient (but
not necessary) conditions that GHC applies to know that typechecking
is decidable.  It just says that if you have a dependency "a -> b",
and you have type variables in b, then they should mention all the
type variables in a.  Thus, the following code is legal without
UndecidableInstances:

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}

class A a b | a -> b
instance A [a] (Either String a)

But the following program is not:

class A a b | a -> b
instance A a (Either String b)

> The answer is that such an instance actually violates the functional
> dependency, but UndecidableInstances just turns off the checks to make
> sure that fundeps are actually functional. It's a, "trust me," switch
> in this case (not just a, "my types might loop," switch).

No, that's not right.  Even with UndecidableInstances, you cannot
define conflicting instances for functional dependencies.  Moreover,
just because the GHC's particular typechecker heuristics don't
guarantee the above code is decidable doesn't mean it isn't decidable.

> So I guess HList will still work fine, and UndecidableInstances are
> actually more evil than I'd previously thought (thanks for the
> correction, Andrea :)).

I think you are thinking of UndecidableInstances in the wrong way.
For instance, the following code does not require
UndecidableInstances, but has polymorphic type variables on the
right-hand side of a functional dependency, which seems to be what you
object to:

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}

class C a b | a -> b where
foo :: a -> b

instance C (Either a b) (Maybe b) where
foo _ = Nothing

bar :: Maybe Int
bar = foo (Left "x")

baz :: Maybe Char
baz = foo (Left "x")

Remember, FunctionalDependencies are all about determining which
instance you select.  The functional dependency "class C a b | a -> b"
says that for a given type a, you can decide which instance of C
(i.e., which particular function foo) to invoke without regard to the
type b.  It specifically does *not* say whether or not type b has to
be grounded, or whether it may include free type variables, including
just being a type variable if you enable FlexibleInstances:

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}

class C a b | a -> b where
foo :: a -> b

instance C (Either a b) b where  -- no UndecidableInstances needed
foo _ = undefined

> > A functional dependency such as "| a b -> c d" just guarantees that a
> > and b uniquely determine the instance.  Hence, it is okay to have
> > class methods that do not mention type variables c and d, because the
> > compiler will still know which instance to pick.
> 
> It specifies that a and b uniquely determine c and d, so the choice of
> instances is unambiguous based only on a and b.

Yes, but it doesn't say the c and d are ground types.  c can be
(Maybe c'), or, with flexible instances it can just be some free type
variable c''.

> This is the basis for type level computation that people do with
> fundeps, because a fundep 'a b -> c' allows one to compute a unique
> c for each pair of types.

Again, unique but not grounded.  Either Maybe c or "forall c" is a
perfectly valid unique type, though it's not grounded.  People do
weird type-level computations with fundeps using types that aren't
forall c.  (The compiler will fail if you additionally have an
instance with forall c.)

> Being allowed to elide variables from the types of methods 

Re: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread Dan Doel
On Tue, Jun 14, 2011 at 1:19 PM,
 wrote:
> No, these are not equivalent.  The first one "TypeEq a b c" is just
> declaring an instance that works "forall c".  The second is declaring
> multiple instances, which, if there were class methods, could have
> different code.  The second one is illegal, because given just the
> first two types, a and b, you cannot tell which instance to pick.

Then why am I not allowed to write:

class C a b | a -> b
instance C T [a]

without undecidable instances? GHC actually complains in that case
that the coverage condition is violated. But it is a single well
specified instance that works for all a.

The answer is that such an instance actually violates the functional
dependency, but UndecidableInstances just turns off the checks to make
sure that fundeps are actually functional. It's a, "trust me," switch
in this case (not just a, "my types might loop," switch).

So I guess HList will still work fine, and UndecidableInstances are
actually more evil than I'd previously thought (thanks for the
correction, Andrea :)).

> A functional dependency such as "| a b -> c d" just guarantees that a
> and b uniquely determine the instance.  Hence, it is okay to have
> class methods that do not mention type variables c and d, because the
> compiler will still know which instance to pick.

It specifies that a and b uniquely determine c and d, so the choice of
instances is unambiguous based only on a and b. This is the basis for
type level computation that people do with fundeps, because a fundep
'a b -> c' allows one to compute a unique c for each pair of types.

If it were just about variable sets determining the instance, then we
could just list those. But I can write:

class C a b c d | a b -> c

And it will actually be a, b and d that determine the particular
instance, but just listing 'a b d', or using the fundep 'a b d -> c'
is wrong, because the fundep above specifies that there is a single
choice of c for each a and b. So we could have:

C Int Int Char Int
C Int Int Char Double
C Int Int Char Float

but trying to add:

C Int Int Int Char

to the first three would be an error, because the first two parameters
determine the third, rather than the first, second and fourth.

Being allowed to elide variables from the types of methods is one of
the uses of fundeps, and probably why they were introduced, but it is
not what fundeps mean.

> On the other hand, though the compiler won't accept it, you could at
> least theoretically allow code such as the following:
>
>        instance C [Char] where
>            type Foo [Char] = forall b. () => b

The fundep equivalent of this is not 'instance C [Char] b'. It is:

instance C [Char] (forall b. b)

except types like that aren't allowed in instances (for good reason in general).

The closest you could come to 'instance C T b' would be something like:

type instance F T = b

except that is probably interpreted by GHC as being (forall b. b).

-- Dan

___
Haskell-prime mailing list
Haskell-prime@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-prime


Re: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread dm-list-haskell-prime
At Tue, 14 Jun 2011 12:31:47 -0400,
Dan Doel wrote:
> 
> Sorry about the double send, David. I forgot to switch to reply-all in
> the gmail interface.
> 
> Okay. I don't really write a lot of code like this, so maybe I missed
> the quirks.
> 
> In that case, HList has been relying on broken behavior of fundeps for
> 7 years. :) Because the instance:
> 
>    instance TypeEq a b c
> 
> violates the functional dependency, by declaring:
> 
>    instance TypeEq Int Int Int
>    instance TypeEq Int Int Char
>    instance TypeEq Int Int Double

No, these are not equivalent.  The first one "TypeEq a b c" is just
declaring an instance that works "forall c".  The second is declaring
multiple instances, which, if there were class methods, could have
different code.  The second one is illegal, because given just the
first two types, a and b, you cannot tell which instance to pick.

>    class C a b | a -> b where
>      foo :: a -> b
>      foo = error "Yo dawg."
> 
>    instance C a b where
> 
>    bar :: Int
>    bar = foo "x"
> 
>    baz :: Char
>    baz = foo "x"
> 
> so we're using an instance C String Int and an instance C String Char
> despite the fact that there's a functional dependency from the first
> argument to the second.

A functional dependency such as "| a b -> c d" just guarantees that a
and b uniquely determine the instance.  Hence, it is okay to have
class methods that do not mention type variables c and d, because the
compiler will still know which instance to pick.

Since your code only has one instance of class C, the unique instance
is trivially guaranteed and the code is fine.  In fact, your code is
effectively the same as:

class C a where
  foo :: a -> b
  foo = error "Yo dawg."

instance C a where

The same issues happen with type families.  The following code is
obviously illegal, because you have a duplicate instance of class C:

class C a where
type Foo a
foo :: a -> Foo a
foo = error "Yo dawg."
instance C [Char] where
type Foo [Char] = Int
instance C [Char] where
type Foo [Char] = Char

On the other hand, though the compiler won't accept it, you could at
least theoretically allow code such as the following:

instance C [Char] where
type Foo [Char] = forall b. () => b

David

___
Haskell-prime mailing list
Haskell-prime@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-prime


Re: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread Andrea Vezzosi
On Tue, Jun 14, 2011 at 6:31 PM, Dan Doel  wrote:
> Sorry about the double send, David. I forgot to switch to reply-all in
> the gmail interface.
>
> On Tue, Jun 14, 2011 at 11:49 AM,
>  wrote:
>> You absolutely still can use FunctionalDependencies to determine type
>> equality in GHC 7.  For example, I just verified the code below with
>> GHC 7.02:
>>
>> *Main> typeEq True False
>> HTrue
>> *Main> typeEq (1 :: Int) (2 :: Int)
>> HTrue
>> *Main> typeEq (1 :: Int) False
>> HFalse
>>
>> As always, you have to make sure one of the overlapping instances is
>> more specific than the other, which you can do by substituting a
>> parameter c for HFalse in the false case and fixing c to HFalse using
>> another class like TypeCast in the context.  (As contexts play no role
>> in instance selection, they don't make the instance any more
>> specific.)
>>
>> While I don't have convenient access to GHC 6 right this second, I'm
>> pretty sure there has been no change for a while, as the HList paper
>> discussed this topic in 2004.
>
> Okay. I don't really write a lot of code like this, so maybe I missed
> the quirks.
>
> In that case, HList has been relying on broken behavior of fundeps for
> 7 years. :) Because the instance:
>
>    instance TypeEq a b c
>
> violates the functional dependency, by declaring:
>
>    instance TypeEq Int Int Int
>    instance TypeEq Int Int Char
>    instance TypeEq Int Int Double
>    ...
>    instance TypeEq Int Char Int
>    instance TypeEq Int Char Char
>    ...
>
> and adding the constraint doesn't actually affect which instances are
> being declared, it just adds a constraint requirement for when any of
> the instances are used.

I think we can argue on the truth of this point, since i don't think
that e.g. instance Ord a => Ord [a] where {...} declares an instance
for Ord [Int -> Int], even if instance resolution won't look at the
context at first.

HList is ensuring the fundep is respected, which has to be done by the
programmer when one is using UndecidableInstances, by relying on the
fact that with OverlappingInstances a more general instance matches
only when a more specific one can be shown not to match (one problem
here is that this assumption is currently broken for type variables
that come out of an existential wrapper, but surprisingly not so for
the equivalent RankNTypes encoding).

-- Andrea

___
Haskell-prime mailing list
Haskell-prime@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-prime


Re: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread Dan Doel
Sorry about the double send, David. I forgot to switch to reply-all in
the gmail interface.

On Tue, Jun 14, 2011 at 11:49 AM,
 wrote:
> You absolutely still can use FunctionalDependencies to determine type
> equality in GHC 7.  For example, I just verified the code below with
> GHC 7.02:
>
> *Main> typeEq True False
> HTrue
> *Main> typeEq (1 :: Int) (2 :: Int)
> HTrue
> *Main> typeEq (1 :: Int) False
> HFalse
>
> As always, you have to make sure one of the overlapping instances is
> more specific than the other, which you can do by substituting a
> parameter c for HFalse in the false case and fixing c to HFalse using
> another class like TypeCast in the context.  (As contexts play no role
> in instance selection, they don't make the instance any more
> specific.)
>
> While I don't have convenient access to GHC 6 right this second, I'm
> pretty sure there has been no change for a while, as the HList paper
> discussed this topic in 2004.

Okay. I don't really write a lot of code like this, so maybe I missed
the quirks.

In that case, HList has been relying on broken behavior of fundeps for
7 years. :) Because the instance:

   instance TypeEq a b c

violates the functional dependency, by declaring:

   instance TypeEq Int Int Int
   instance TypeEq Int Int Char
   instance TypeEq Int Int Double
   ...
   instance TypeEq Int Char Int
   instance TypeEq Int Char Char
   ...

and adding the constraint doesn't actually affect which instances are
being declared, it just adds a constraint requirement for when any of
the instances are used. It appears I was wrong, though. GHC doesn't
actually fix the instance for such fundeps, and the following compiles
and runs fine:

   class C a b | a -> b where
     foo :: a -> b
     foo = error "Yo dawg."

   instance C a b where

   bar :: Int
   bar = foo "x"

   baz :: Char
   baz = foo "x"

so we're using an instance C String Int and an instance C String Char
despite the fact that there's a functional dependency from the first
argument to the second.

-- Dan

___
Haskell-prime mailing list
Haskell-prime@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-prime


Re: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread dm-list-haskell-prime
At Tue, 14 Jun 2011 10:40:48 -0400,
Dan Doel wrote:
> 
> > 1. As things stand in GHC you can do some things with functional 
> > dependencies that you can't do with type families. The archetypical example 
> > is type equality.  We cannot write
> >        type family Eq a b :: *
> >        type instance Eq k k = True
> >        type instance Eq j k = False
> > because the two instances overlap.  But you can with fundeps
> >        class Eq a b c | a b -> c
> >        instance Eq k k True
> >        instance Eq j k False
> > As Alexey mentioned, fundeps have other disadvantages, so it's reasonable 
> > to ask whether type families could extend to handle cases like this.
> 
> Fundeps no longer allow this as of GHC 7, it seems. It causes the same
> fundep violation as the case:
> 
> class C a b | a -> b
> instance C a R
> instance C T U

You absolutely still can use FunctionalDependencies to determine type
equality in GHC 7.  For example, I just verified the code below with
GHC 7.02:

*Main> typeEq True False
HTrue
*Main> typeEq (1 :: Int) (2 :: Int)
HTrue
*Main> typeEq (1 :: Int) False
HFalse

As always, you have to make sure one of the overlapping instances is
more specific than the other, which you can do by substituting a
parameter c for HFalse in the false case and fixing c to HFalse using
another class like TypeCast in the context.  (As contexts play no role
in instance selection, they don't make the instance any more
specific.)

While I don't have convenient access to GHC 6 right this second, I'm
pretty sure there has been no change for a while, as the HList paper
discussed this topic in 2004.

David


{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE OverlappingInstances #-}
{-# LANGUAGE UndecidableInstances #-}

class TypeCast a b | a -> b where
typeCast :: a -> b
instance TypeCast a a where
typeCast = id

data HTrue = HTrue deriving (Show)
data HFalse = HFalse deriving (Show)

class TypeEq a b c | a b -> c where
typeEq :: a -> b -> c
instance TypeEq a a HTrue where
typeEq _ _ = HTrue
instance (TypeCast HFalse c) => TypeEq a b c where
typeEq _ _ = typeCast HFalse


___
Haskell-prime mailing list
Haskell-prime@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-prime


Re: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread Dan Doel
On Tue, Jun 14, 2011 at 11:27 AM, Andrea Vezzosi  wrote:
>>    class C a b | a -> b
>>    instance C a R
>>    instance C T U
>
> Are you sure that worked before?

80%

> The following still does anyhow:
>
>    data R
>    data T
>    data U
>    class C a b | a -> b
>    instance TypeCast R b => C a b
>    instance TypeCast U b => C T b
>
> In fact this is how IsFunction was implemented in 2005[1], and the
> same transformation appears to work for the Eq class too.
> If we allow TypeFamilies we can use (~) instead of the TypeCast hack,
> fortunately.

So it does.

instance (b ~ R) => C a b
instance (b ~ U) => C T b

Which is odd. I don't think there's a way to justify this working.
Either the preconditions are being taken into account, in which case
there is no reason for this to behave differently from:

instance C a R
instance C T U

or the preconditions are not being taken into account (which is the
type class way), in which case both of the qualified instances are
illegal, because they declare instances C T b for all b (plus a
constraint on the use), which violates the fundep. I've seen examples
like this before, and I think what GHC ends up doing (now) is fixing
the 'b' to whatever instance it needs first. But I don't think that's
very good behavior.

In this case it happens that it's impossible to use at more than one
instance, but if you accept the instances, you're back to the
questions of type soundness that are only evaded because fundeps don't
actually use all the information they allegedly express.

-- Dan

___
Haskell-prime mailing list
Haskell-prime@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-prime


Re: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread dm-list-haskell-prime
At Tue, 14 Jun 2011 09:36:41 +,
Simon Peyton-Jones wrote:
> 
> 5.  David wants a wiki page fixed.  But which one? And how is it "locked 
> down"?

This page:


http://hackage.haskell.org/trac/haskell-prime/wiki/FunctionalDependencies

Currently under cons for FunctionalDependencies, it says:

AssociatedTypes seem to be more promising.

I proposed the following fix:

AssociatedTypes seem to be more promising, but in their
current form are not as general as FunctionalDependencies
[link].

where the link points to this or another email thread.

Is there a policy that only a proposal's "owner" can modify the wiki
page?  Or that you have to be a member of the Haskell' committee?  At
any rate, when I log into the Haskell' wiki, I don't get an Edit
button.  That's all I meant by locked down.

David

___
Haskell-prime mailing list
Haskell-prime@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-prime


Re: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread Andrea Vezzosi
On Tue, Jun 14, 2011 at 4:40 PM, Dan Doel  wrote:
> On Tue, Jun 14, 2011 at 5:36 AM, Simon Peyton-Jones
>  wrote:
>> There was an interesting thread on haskell-prime [1], about the relationship 
>> between functional dependencies and type families.  This message is my 
>> attempt to summarise the conclusions of that thread.  I'm copying other 
>> interested parties (eg Oleg, Dimitrios)
>>  [1] http://www.haskell.org/pipermail/haskell-prime/2011-May/003416.html
>>
>> 1. As things stand in GHC you can do some things with functional 
>> dependencies that you can't do with type families. The archetypical example 
>> is type equality.  We cannot write
>>        type family Eq a b :: *
>>        type instance Eq k k = True
>>        type instance Eq j k = False
>> because the two instances overlap.  But you can with fundeps
>>        class Eq a b c | a b -> c
>>        instance Eq k k True
>>        instance Eq j k False
>> As Alexey mentioned, fundeps have other disadvantages, so it's reasonable to 
>> ask whether type families could extend to handle cases like this.
>
> Fundeps no longer allow this as of GHC 7, it seems. It causes the same
> fundep violation as the case:
>
>    class C a b | a -> b
>    instance C a R
>    instance C T U

Are you sure that worked before? The following still does anyhow:

{-# LANGUAGE MultiParamTypeClasses, OverlappingInstances,
FunctionalDependencies
, EmptyDataDecls, FlexibleInstances,
UndecidableInstances #-}

class TypeCast   a b   | a -> b, b->a   where typeCast   :: a -> b
class TypeCast'  t a b | t a -> b, t b -> a where typeCast'  :: t->a->b
class TypeCast'' t a b | t a -> b, t b -> a where typeCast'' :: t->a->b
instance TypeCast'  () a b => TypeCast a b where typeCast x = typeCast' () x
instance TypeCast'' t a b => TypeCast' t a b where typeCast' = typeCast''
instance TypeCast'' () a a where typeCast'' _ x  = x

data R
data T
data U
class C a b | a -> b
instance TypeCast R b => C a b
instance TypeCast U b => C T b

In fact this is how IsFunction was implemented in 2005[1], and the
same transformation appears to work for the Eq class too.
If we allow TypeFamilies we can use (~) instead of the TypeCast hack,
fortunately.

[1] http://okmij.org/ftp/Haskell/isFunction.lhs

-- Andrea

> for R /~ U. Which I find somewhat sensible, because the definitions
> together actually declare two relations for any type:
>
>    Eq T T False
>    Eq T T True
>
> and we are supposed to accept that because one is in scope, and has a
> particular form, the other doesn't exist. But they needn't always be
> in scope at the same time.
>
> Essentially, GHC 7 seems to have separated the issue of type-function
> overlapping, which is unsound unless you have specific conditions that
> make it safe---conditions which fundeps don't actually ensure (fundeps
> made it 'safe' in the past by not actually doing all the computation
> that type families do)---from the issue of instance overlapping, which
> is always sound at least. But if I'm not mistaken, type families can
> handle these cases.
>
> I'd personally say it's a step in the right direction, but it probably
> breaks a lot of HList-related stuff.
>
> -- Dan
>
> ___
> Haskell-prime mailing list
> Haskell-prime@haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-prime
>

___
Haskell-prime mailing list
Haskell-prime@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-prime


Re: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread Dan Doel
On Tue, Jun 14, 2011 at 5:36 AM, Simon Peyton-Jones
 wrote:
> There was an interesting thread on haskell-prime [1], about the relationship 
> between functional dependencies and type families.  This message is my 
> attempt to summarise the conclusions of that thread.  I'm copying other 
> interested parties (eg Oleg, Dimitrios)
>  [1] http://www.haskell.org/pipermail/haskell-prime/2011-May/003416.html
>
> 1. As things stand in GHC you can do some things with functional dependencies 
> that you can't do with type families. The archetypical example is type 
> equality.  We cannot write
>        type family Eq a b :: *
>        type instance Eq k k = True
>        type instance Eq j k = False
> because the two instances overlap.  But you can with fundeps
>        class Eq a b c | a b -> c
>        instance Eq k k True
>        instance Eq j k False
> As Alexey mentioned, fundeps have other disadvantages, so it's reasonable to 
> ask whether type families could extend to handle cases like this.

Fundeps no longer allow this as of GHC 7, it seems. It causes the same
fundep violation as the case:

class C a b | a -> b
instance C a R
instance C T U

for R /~ U. Which I find somewhat sensible, because the definitions
together actually declare two relations for any type:

Eq T T False
Eq T T True

and we are supposed to accept that because one is in scope, and has a
particular form, the other doesn't exist. But they needn't always be
in scope at the same time.

Essentially, GHC 7 seems to have separated the issue of type-function
overlapping, which is unsound unless you have specific conditions that
make it safe---conditions which fundeps don't actually ensure (fundeps
made it 'safe' in the past by not actually doing all the computation
that type families do)---from the issue of instance overlapping, which
is always sound at least. But if I'm not mistaken, type families can
handle these cases.

I'd personally say it's a step in the right direction, but it probably
breaks a lot of HList-related stuff.

-- Dan

___
Haskell-prime mailing list
Haskell-prime@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-prime


RE: TypeFamilies vs. FunctionalDependencies & type-level recursion

2011-06-14 Thread Simon Peyton-Jones
There was an interesting thread on haskell-prime [1], about the relationship 
between functional dependencies and type families.  This message is my attempt 
to summarise the conclusions of that thread.  I'm copying other interested 
parties (eg Oleg, Dimitrios)
  [1] http://www.haskell.org/pipermail/haskell-prime/2011-May/003416.html

1. As things stand in GHC you can do some things with functional dependencies 
that you can't do with type families. The archetypical example is type 
equality.  We cannot write
type family Eq a b :: *
type instance Eq k k = True
type instance Eq j k = False
because the two instances overlap.  But you can with fundeps
class Eq a b c | a b -> c
instance Eq k k True
instance Eq j k False
As Alexey mentioned, fundeps have other disadvantages, so it's reasonable to 
ask whether type families could extend to handle cases like this.

2.  In a hypothetical extension to type families, namely *closed* type 
families, you could support overlap:
closed type family Eq a b :: * where
  type instance Eq k k = True
  type instance Eq j k = False
The idea is that you give all the equations together; matching is 
top-to-bottom; and the type inference only picks an equation when all the 
earlier equations are guaranteed not to match.  So there is no danger of making 
different choices in different parts of the program (which is what gives rise 
to unsoundness).

3.  Closed type families are not implemented yet.  The only tricky point I see 
would be how to express in System FC the proof that "earlier equations don't 
match".   Moreover, to support abstraction one might well want David's /~ 
predicate, so that you could say
f :: forall a b. (a /~ b) => ..blah..
This f can be applied to any types a,b provided they don't match.   I'm frankly 
unsure about all the consequences here.

4.  As Roman points out, type families certainly do support recursion; it's 
just they don't support overlap.

5.  David wants a wiki page fixed.  But which one? And how is it "locked down"?

6. There is a very old Trac wiki page about "total" type families here
http://hackage.haskell.org/trac/ghc/wiki/TypeFunctions/TotalFamilies
Written by Manuel, I think it needs updating.

That's my summary!

Simon

___
Haskell-prime mailing list
Haskell-prime@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-prime