Re: [Haskell] View patterns in GHC: Request for feedback

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
51 messages Options
123
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] View patterns in GHC: Request?for?feedback

haskell-2
And the readability is destroyed because you cannot do any type inference in
your head.

If you see

{
 Matrix m = ....;
 Matrix x = m * y;
 ...;
}

Then you know very little about the possible types of y since can only conclude
that:

Matrix can be multiplied by one or more types 'sometype' becuase it has one or
more (operator *(sometype x)) methods defined.

And 'y' is either one of these sometypes's or _any_ other class that matches
_any_ single argument constructor of any of those sometypes (except for those
constructors are marked 'explicit').

Now you need to read the header definitions for Matrix and the headers for every
type that can multiply Matrix.  Only after that can you say what set of types
the 'y' might be.

Now do this for every argument of every method call and every operator in the code.

This is part of the reason that shops that use C++ often have a long list of
features that must never be used.  This is part of the reason that new people
who use C++ are notorious because they produce code that uses too many of C++
features.  Code written in arbitrary C++ is unreadable.

Aaron Denney wrote:

> On 2007-07-27, David Roundy <[hidden email]> wrote:
>> The solution is to add explicit to the constructor for all single-argument
>> constructors (except perhaps occasionally when you actually want explicit
>> construction of objects).
>>
>> The reasoning behind this, of course, is to allow nice interactions of
>> home-made classes such as complex numbers, or string classes (which you
>> might want to be automatically constructed from string constants).
>
> I'd have thought that adding an "implicit" keyword would make more
> sense, and only do conversions then.  But I forget, C++.
>


_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Re: [Haskell] View patterns in GHC: Requestforfeedback

Claus Reinke
In reply to this post by Dan Licata
> Oh!  I had assumed that it was already considered rude to expose a
> non-exhaustive function to the outside world:

you mean, as in: head, tail, fromJust, ..?-)

whether exposing or using those is considered rude or not, the type
system has nothing to tell us about their not handling some inputs,
and if you get them from precompiled libraries, you don't see the
compiler warnings, either.

>> one could turn that promise into a type-checked guarantee by using
>> explicit sum types (and thus structural rather than name-based typing),
>> but that gets awkward in plain haskell.
>
> I don't think the choice of whether you label your variants with names
> or with numbers (in1, in2, in3...) has anything to do with the choice of
> whether you require your cases to be exhaustive or not.

i was talking about name-based (as in: this is the sum type named List)
vs structural (as in: this is the sum type build from Cons and Nil) typing.
the former hides (in-)exhaustiveness from the type system, the latter
exposes it.

consider the example code below, where the type system catches the
non-exhaustiveness of g, where the sum structure of its parameter is
exposed, but has nothing to say about f, where the sum structure is
hidden behind a type name.
 
claus

{-# OPTIONS_GHC -fallow-overlapping-instances #-}
{-# OPTIONS_GHC -fglasgow-exts #-}

{- our own, nameless sum type with injection and unpacking -}
infixr :|
data a :| b
data a :< b = Inj a deriving Show

class    Inj a b                 where { inj   :: a -> a:<b ; out         :: a:<b -> a }
instance Inj a a                 where { inj a  = Inj a     ; out (Inj a)  = a }
instance Inj a (a:|b)            where { inj a  = Inj a     ; out (Inj a)  = a }
instance Inj a b => Inj a (c:|b) where { inj a  = Inj a     ; out (Inj a)  = a }

{- a product type of nested pairs, with selection -}
class    Sel a b                where sel       :: b -> a
instance Sel a a                where sel a      = a
instance Sel a (a,b)            where sel (a,_)  = a
instance Sel a b => Sel a (c,b) where sel (_,b)  = sel b

{- to match, supply a product of functions covering the sum -}
match :: (Inj x xs, Sel (x->b) fs) => x:<xs -> fs -> b
match x fs = sel fs (out x)

{- example A: nameless sum -}
type Sum = String :| Char :| Bool

g y = match y (\s->s::String,
              (\c->c:""
              ))
              -- ,(\b->if b then "1" else "2")))

{- example B: named sum -}
data NamedSum = S String | C Char | B Bool deriving Show

f :: NamedSum -> String
f (S s) = s
f (C c) = c:""
-- f (B b) = if b then "1" else "2"

{- testing -}
main = do
  putStrLn $ g (inj "hi"  :: String:<Sum)
  putStrLn $ f (S "ho"    :: NamedSum)
  putStrLn $ g (inj 'x'   :: Char:<Sum)
  putStrLn $ f (C 'y'     :: NamedSum)
  putStrLn $ g (inj False :: Bool:<Sum)
  putStrLn $ f (B True    :: NamedSum)

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Re: [Haskell] View patterns in GHC: Request for feedback

Dan Licata
In reply to this post by Stefan O'Rear
With the functional dependency, you can't work with the view datatypes
at all.  Once you write

type Typ
data TypView = Unit | Arrow Typ Typ

instance View Typ TypView where
  view = ...

you're no longer allowed to take apart a TypView at all!

E.g. you can't write

outUnit :: TypView -> Bool
outUnit Unit = True
outUnit _    = False

because the implicit application of the view function will mean that
outUnit must consume a Typ.  

Personally, I'd rather have special syntax in the pattern (-> pat) than
have these global effects on what you can do with certain types.

-Dan


On Jul27, Stefan O'Rear wrote:

> On Fri, Jul 27, 2007 at 05:22:37AM -0400, Dan Licata wrote:
> > On Jul26, Stefan O'Rear wrote:
> > > > So, this syntax affects a lot of code, existing or otherwise, that
> > > > doesn't use view patterns, which is something we're trying to avoid.
> > >
> > > Eh?  I *think* the typing rules are the same for the no-view case.  If
> > > the auto-deriving hack isn't implemented, you only need a
> > > deriving(View), otherwise there should be no change at all...
> >
> > Assuming you don't have the functional dependency: "affects" in the
> > sense that any code you write has a generalized type, so you have to
> > explain view patterns to beginners right out of the gate, etc.  If you
> > write
> >
> > length [] = []
> > length (h : t) = 1 + length t
> >
> > we don't want to have to explain to beginners why it has type
> >
> > length :: forall a,b,c. View a [b] -> a -> Num c
>
> Right, which is why I think the functional dependency is good.  If we
> have it, and the auto-deriving hack, what breaks?
>
> length [] = []
> length (h : t) = 1 + length t
>
> length :: forall a b c. (View a [b], Num c) => a -> c
>
> ==> (one of the FD rules)
>
> length :: forall a b c. (View [a] [b], Num c) => [a] -> c
>
> ==> (plain context reduction, the first constraint is tautological)
>
> length :: forall a c. Num c => [a] -> c
>
> Stefan



> _______________________________________________
> Haskell-Cafe mailing list
> [hidden email]
> http://www.haskell.org/mailman/listinfo/haskell-cafe

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Re: [Haskell] View patterns in GHC: Requestforfeedback

Dan Licata
In reply to this post by Claus Reinke
On Jul30, Claus Reinke wrote:

> >>one could turn that promise into a type-checked guarantee by using
> >>explicit sum types (and thus structural rather than name-based typing),
> >>but that gets awkward in plain haskell.
> >
> >I don't think the choice of whether you label your variants with names
> >or with numbers (in1, in2, in3...) has anything to do with the choice of
> >whether you require your cases to be exhaustive or not.
>
> i was talking about name-based (as in: this is the sum type named List)
> vs structural (as in: this is the sum type build from Cons and Nil) typing.
> the former hides (in-)exhaustiveness from the type system, the latter
> exposes it.
>

I don't think the juxtaposition you're setting up here, between
"name-based" and "structural", is correct.  You could just as well
choose the elimination form for "name-based" sum types (i.e., datatypes)
to be an exhaustive case analysis.  It just happens that Haskell chooses
to permit non-exhaustive case expressions.  

-Dan
_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] View patterns in GHC: Request?for?feedback

Jon Fairbairn
In reply to this post by haskell-2
ChrisK <[hidden email]> writes:

> And the readability is destroyed because you cannot do any type inference in
> your head.
>
> If you see
>
> {
>  Matrix m = ....;
>  Matrix x = m * y;
>  ...;
> }
>
> Then you know very little about the possible types of y
> since can only conclude that:

[snippage] This is all very horrid, but as far as I can tell
what I was proposing wouldn't lead to such a mess, except
possibly via defaulting, which, as the least important
aspect of the idea could easily be abandoned.

--
Jón Fairbairn                                 [hidden email]


_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Re: [Haskell] View patterns in GHC: Request for feedback

Stefan O'Rear
In reply to this post by Dan Licata
On Mon, Jul 30, 2007 at 05:31:40AM -0400, Dan Licata wrote:

> With the functional dependency, you can't work with the view datatypes
> at all.  Once you write
>
> type Typ
> data TypView = Unit | Arrow Typ Typ
>
> instance View Typ TypView where
>   view = ...
>
> you're no longer allowed to take apart a TypView at all!
Exactly.  And I'm 100% convinced it's a non-issue, since all the
heavyweight view proposals don't allow you to manipulate view objects
*at all*.  My approach is no worse.

> E.g. you can't write
>
> outUnit :: TypView -> Bool
> outUnit Unit = True
> outUnit _    = False
>
> because the implicit application of the view function will mean that
> outUnit must consume a Typ.  

What would you use it for, anyway?  TypView objects don't exist
anywhere except internally in case-expressions.

> Personally, I'd rather have special syntax in the pattern (-> pat) than
> have these global effects on what you can do with certain types.

You can only declare the instance for TypView in the same module as
TypView itself, since otherwise it would conflict with the implicit
instance.  Therefore, providing an instance is no more "global" than
just renaming the type.

Stefan

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe

signature.asc (196 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Re: [Haskell] View patterns in GHC: Request?for?feedback

David Roundy-2
In reply to this post by Jon Fairbairn
On Mon, Jul 30, 2007 at 11:47:46AM +0100, Jon Fairbairn wrote:

> ChrisK <[hidden email]> writes:
>
> > And the readability is destroyed because you cannot do any type inference in
> > your head.
> >
> > If you see
> >
> > {
> >  Matrix m = ....;
> >  Matrix x = m * y;
> >  ...;
> > }
> >
> > Then you know very little about the possible types of y
> > since can only conclude that:
>
> [snippage] This is all very horrid, but as far as I can tell
> what I was proposing wouldn't lead to such a mess, except
> possibly via defaulting, which, as the least important
> aspect of the idea could easily be abandoned.

What your suggestion would do would be to make the type inferred for every
pattern-matched function polymorphic, which means that in order to
determine the correctness of a function you'd need to examine all other
modules.  Similarly, if you fail to include a type signature in some simple
pattern-matched function in a where clause, adding an import of another
module could make that function fail to compile (with an undeterminable
type error).

This isn't so horrid as C++, but also isn't nearly so beautiful as Haskell.
Admittedly, adding a type signature will make a function verifiably
correct, and avoid any of these ambiguities, but we really like type
inference, and it'd be a shame to introduce code that makes type inference
less powerful.

True, one could always forbid people to use the View class, but that sort
of defeats the purpose, and starts sounding once more like C++, where there
are language features that "shouldn't" be used... but just imagine what
would happen to your type checking, if someone decided that it'd be clever
to use [a] as a view for Integer using a Peano representation? Yikes! (Or
Integer as a view for [a] describing the length?)

Admittedly, havoc would also be wreaked if someone declared [a] to be an
instance of Num, and that's the risk one takes when using type
classes... but that's why it's nice that there is a convenient way to write
code that *doesn't* use type classes.
--
David Roundy
Department of Physics
Oregon State University
_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Re: [Haskell] View patterns in GHC: Request?for?feedback

Stefan O'Rear
On Tue, Jul 31, 2007 at 03:31:54PM -0700, David Roundy wrote:

> On Mon, Jul 30, 2007 at 11:47:46AM +0100, Jon Fairbairn wrote:
> > ChrisK <[hidden email]> writes:
> >
> > > And the readability is destroyed because you cannot do any type inference in
> > > your head.
> > >
> > > If you see
> > >
> > > {
> > >  Matrix m = ....;
> > >  Matrix x = m * y;
> > >  ...;
> > > }
> > >
> > > Then you know very little about the possible types of y
> > > since can only conclude that:
> >
> > [snippage] This is all very horrid, but as far as I can tell
> > what I was proposing wouldn't lead to such a mess, except
> > possibly via defaulting, which, as the least important
> > aspect of the idea could easily be abandoned.
>
> What your suggestion would do would be to make the type inferred for every
> pattern-matched function polymorphic, which means that in order to
> determine the correctness of a function you'd need to examine all other
> modules.  Similarly, if you fail to include a type signature in some simple
> pattern-matched function in a where clause, adding an import of another
> module could make that function fail to compile (with an undeterminable
> type error).
Excuse me?  One of the most critical properties of type classes is that
adding new instances can never make old code that uses old instances
stop compiling; the worst you could get is a definition conflict.

Stefan

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe

signature.asc (196 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Re: [Haskell] View patterns in GHC: Request?for?feedback

David Roundy-2
On Tue, Jul 31, 2007 at 04:04:17PM -0700, Stefan O'Rear wrote:

> On Tue, Jul 31, 2007 at 03:31:54PM -0700, David Roundy wrote:
> > On Mon, Jul 30, 2007 at 11:47:46AM +0100, Jon Fairbairn wrote:
> > > ChrisK <[hidden email]> writes:
> > >
> > > > And the readability is destroyed because you cannot do any type inference in
> > > > your head.
> > > >
> > > > If you see
> > > >
> > > > {
> > > >  Matrix m = ....;
> > > >  Matrix x = m * y;
> > > >  ...;
> > > > }
> > > >
> > > > Then you know very little about the possible types of y
> > > > since can only conclude that:
> > >
> > > [snippage] This is all very horrid, but as far as I can tell
> > > what I was proposing wouldn't lead to such a mess, except
> > > possibly via defaulting, which, as the least important
> > > aspect of the idea could easily be abandoned.
> >
> > What your suggestion would do would be to make the type inferred for every
> > pattern-matched function polymorphic, which means that in order to
> > determine the correctness of a function you'd need to examine all other
> > modules.  Similarly, if you fail to include a type signature in some simple
> > pattern-matched function in a where clause, adding an import of another
> > module could make that function fail to compile (with an undeterminable
> > type error).
>
> Excuse me?  One of the most critical properties of type classes is that
> adding new instances can never make old code that uses old instances
> stop compiling; the worst you could get is a definition conflict.
I see that I was wrong.  I was thinking of something like

foo :: C a => Int -> a
bar :: C a => a -> Int
baz :: Int -> Int

baz = bar . foo

and that this would compile if there was only one instance of class C.  But
I see that in fact it will fail to compile regardless, which makes sense.
--
David Roundy
Department of Physics
Oregon State University

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe

signature.asc (196 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] View patterns in GHC: Request?for?feedback

haskell-2
In reply to this post by Stefan O'Rear
Discussion continues below quoted text...

Stefan O'Rear wrote:

> On Tue, Jul 31, 2007 at 03:31:54PM -0700, David Roundy wrote:
>> On Mon, Jul 30, 2007 at 11:47:46AM +0100, Jon Fairbairn wrote:
>>> ChrisK <[hidden email]> writes:
>>>
>>>> And the readability is destroyed because you cannot do any type inference in
>>>> your head.
>>>>
>>>> If you see
>>>>
>>>> {
>>>>  Matrix m = ....;
>>>>  Matrix x = m * y;
>>>>  ...;
>>>> }
>>>>
>>>> Then you know very little about the possible types of y
>>>> since can only conclude that:
>>> [snippage] This is all very horrid, but as far as I can tell
>>> what I was proposing wouldn't lead to such a mess, except
>>> possibly via defaulting, which, as the least important
>>> aspect of the idea could easily be abandoned.
>> What your suggestion would do would be to make the type inferred for every
>> pattern-matched function polymorphic, which means that in order to
>> determine the correctness of a function you'd need to examine all other
>> modules.  Similarly, if you fail to include a type signature in some simple
>> pattern-matched function in a where clause, adding an import of another
>> module could make that function fail to compile (with an undeterminable
>> type error).
>
> Excuse me?  One of the most critical properties of type classes is that
> adding new instances can never make old code that uses old instances
> stop compiling; the worst you could get is a definition conflict.
>
> Stefan

That is true, and it is one of of the perfect things about type classes.  But
that is not the problem with some of the proposals to make normal syntax
patterns always look for a View inference.  The problem is that to read any
pattern match I have to know which View instance was chosen.  So each and every
pattern match requires me to run a type checker in my head which draws on all
the View instances which were implicitly imported into the current module.  That
adding another invisible instance does not break the code is irrelevant, the
problem is that huge amount of effort it takes the human reader to prove to
herself or himself that any particular pattern match has been correctly understood.

It should require a new syntax to invoke this complexity.

--
Chris

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] View patterns in GHC: Request?for?feedback

Jon Fairbairn
In reply to this post by David Roundy-2
David Roundy <[hidden email]> writes:

> On Mon, Jul 30, 2007 at 11:47:46AM +0100, Jon Fairbairn wrote:
> > [snippage] This is all very horrid, but as far as I can tell
> > what I was proposing wouldn't lead to such a mess, except
> > possibly via defaulting, which, as the least important
> > aspect of the idea could easily be abandoned.
>
> What your suggestion would do would be to make the type inferred for every
> pattern-matched function polymorphic,

No it wouldn't. I reiterate: constructors would either
belong to a class or to a datatype, so for constructors that
belong to a datatype the situation would be exactly as
before.

It's unfortunate that when I wrote my example I assumed that
everyone would understand that for some classes we would
have defaults (because we already have this: the
constructors for Integer effectively belong to a class and
are defaulted to Integer). I'm now quite convinced that
using defaults together with more general overloaded
constructors would be a mistake, but that's all you've
managed to convince me of!

Yes, there is a problem that importing a class with a
constructor into a scope that declares the same constructor
as a data constructor would cause difficulties (namely the
module would nolonger compile), but aren't they exactly the
same difficulties as when the name in question is just a
function name?

--
Jón Fairbairn                                 [hidden email]

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
123