newbie type signature question

classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|

newbie type signature question

Brock Peabody
Please excuse my newbiness, but in this snippet:


   data (Monad m) => DataType m = DataType { f :: Char -> m () }
                 
   test_function :: (Monad m) => DataType m -> m ()
                    ^^^^^^^^^^^^
   test_function d =  f d 'C'



Why is "(Monad m) =>" required, when the definition of DataType already
implies it?  Is there an easier way to do this or will I have to have it
in all signatures containing DataType?

Thanks,

Brock

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

Re: newbie type signature question

Brian Hulley
Brock Peabody wrote:

> Please excuse my newbiness, but in this snippet:
>
>
>   data (Monad m) => DataType m = DataType { f :: Char -> m () }
>
>   test_function :: (Monad m) => DataType m -> m ()
>                    ^^^^^^^^^^^^
>   test_function d =  f d 'C'
>
>
>
> Why is "(Monad m) =>" required, when the definition of DataType
> already implies it?  Is there an easier way to do this or will I have
> to have it in all signatures containing DataType?

This is exactly the same question I had when I started learning Haskell.
The reason is that you are explicitly giving a type annotation for
test_function, and whenever you give a type annotation what you see is what
you get: the compiler doesn't add any extra things to it.

If you had not given any type signature the inferred type would have been

    Monad m => DataType m -> m ()

as expected, whereas when you wrote

    test_function :: DataType m -> m ()

you are actively telling the compiler there is no context at all, but the
compiler needs the context in this particular function in order to apply f.
The context is not needed in all functions which use DataType - only in
those functions which actually make use of m's Monad'ness, so you could
write:

    test2 :: DataType m -> DataType m
    test2 x = x

(See http://haskell.org/onlinereport/decls.html#user-defined-datatypes 
section 4.2.1 for more info)

There was a post a while back (unfortunately I can't seem to locate it)
where someone posted a link to some guidelines on haskell coding style where
one guideline was never to use contexts in data declarations.

Regards, Brian.

--
Logic empowers us and Love gives us purpose.
Yet still phantoms restless for eras long past,
congealed in the present in unthought forms,
strive mightily unseen to destroy us.

http://www.metamilk.com 

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

RE: newbie type signature question

Brock Peabody
> Brian Hulley wrote:

> There was a post a while back (unfortunately I can't seem to locate
it)
> where someone posted a link to some guidelines on haskell coding style
> where
> one guideline was never to use contexts in data declarations.

I would love to see that guideline.  What is the correct way to express
a constraint for a data declaration if this way is wrong?

Thanks,

Brock

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

Re[2]: newbie type signature question

Bulat Ziganshin-2
In reply to this post by Brian Hulley
Hello Brian,

Friday, June 9, 2006, 9:50:30 PM, you wrote:

>>   data (Monad m) => DataType m = DataType { f :: Char -> m () }
>>
>>   test_function :: (Monad m) => DataType m -> m ()
>>                    ^^^^^^^^^^^^
> There was a post a while back (unfortunately I can't seem to locate it)
> where someone posted a link to some guidelines on haskell coding style where
> one guideline was never to use contexts in data declarations.

afaiu, this is considered as drawback of H98, and may be even
addressed some day. at least in h' mail-list it was discussed: adding
the datatype context to the context of functions that use it. we can
even ask GHC team to add this as one more glasgow extension




--
Best regards,
 Bulat                            mailto:[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: newbie type signature question

Brock Peabody
In reply to this post by Brock Peabody
> From: Brandon Moore

> Getting them both is tricky, but you can do it if you use a GADT to
> write a type that means "exists a such that a = m and a is a Monad":

Is GADT a way to assemble types at compile-time?  It looks really cool.

> {-# OPTIONS -fglasgow-exts #-}
> data TyEq (a :: * -> *) (b :: * -> *) where
                  ^
                  ^

Compiling this fails here (the first '*') for me with "parse error on
input '*'" (ghc 6.4.1), but I'll keep playing with it.

Thanks,

Brock


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

Re: newbie type signature question

Brandon Moore
In reply to this post by Brock Peabody
Sorry, I meant to send this to the whole list.

Brock Peabody wrote:

> Please excuse my newbiness, but in this snippet:
>
>
>    data (Monad m) => DataType m = DataType { f :: Char -> m () }
>                  
>    test_function :: (Monad m) => DataType m -> m ()
>                     ^^^^^^^^^^^^
>    test_function d =  f d 'C'
>
>
>
> Why is "(Monad m) =>" required, when the definition of DataType already
> implies it?  Is there an easier way to do this or will I have to have it
> in all signatures containing DataType?

Because class constraints on data types are a bit silly. It just
restricts the types of the constructors so you can only apply them if m
is a Monad. It doesn't actually package up the evidance that m is a
monad inside the value to make this stuff work.

Existential types do package up the class instance in the value, but
they hide the type.

data Showable = forall a . (Show a) => Showable a
showShowable :: Showable -> String
showShowable (Showable x) = show x

Getting them both is tricky, but you can do it if you use a GADT to
write a type that means "exists a such that a = m and a is a Monad":

{-# OPTIONS -fglasgow-exts #-}
data TyEq (a :: * -> *) (b :: * -> *) where
   Refl :: TyEq a a

data DataType m = forall m' . (Monad m') => DataType (TyEq m m') (Char
-> m' ())

buildDataType :: (Monad m) => (Char -> m ()) -> DataType m
buildDataType = DataType Refl

test_function :: DataType m -> m ()
test_function (DataType Refl f) = f 'C'

-- try let x = buildDataType putChar
--     :t x
--     :t test_function
--     test_function x

Brandon

> Thanks,
>
> Brock
>
> _______________________________________________
> 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: newbie type signature question

Brian Hulley
In reply to this post by Brock Peabody
Brock Peabody wrote:
>> Brian Hulley wrote:
>
>> There was a post a while back (unfortunately I can't seem to locate
>> it) where someone posted a link to some guidelines on haskell coding
>> style where
>> one guideline was never to use contexts in data declarations.
>
> I would love to see that guideline.  What is the correct way to
> express a constraint for a data declaration if this way is wrong?

It is at
http://www.informatik.uni-bremen.de/agbkb/forschung/formal_methods/CoFI/hets/src-distribution/daily/HetCATS/docs/Programming-Guidelines.txt 
in the section under "Types":

  "Don't put class constraints on a data type,
   constraints belong only to the functions
   that manipulate the data."

So according to this guideline you're not supposed to think of associating
contraints with data: constraints are only relevant for functions which
manipulate the data, therefore (if you agree with this view) the very idea
of associating constraints with data is wrong.

It is possible that this feature was added to the language for the benefit
of people who prefer not to use explicit type signatures but afaiu this goes
against best practice where everything should always have an explicit
signature to make code easy to understand and facilitate debugging of type
errors.

Of course this does not apply to the completely different use of constraints
when defining existential types.

Regards, Brian.

--
Logic empowers us and Love gives us purpose.
Yet still phantoms restless for eras long past,
congealed in the present in unthought forms,
strive mightily unseen to destroy us.

http://www.metamilk.com 

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

Re[2]: newbie type signature question

Bulat Ziganshin-2
Hello Brian,

Saturday, June 10, 2006, 3:05:25 AM, you wrote:

> It is possible that this feature was added to the language for the benefit
> of people who prefer not to use explicit type signatures but afaiu this goes
> against best practice where everything should always have an explicit
> signature to make code easy to understand and facilitate debugging of type
> errors.

when you work with C++ or some other OOP language, you can define that
some field in structure should some some specific interface and this
allows to use functions of this interface on this field. i required
the same feature in Haskell, for example:

data UTF8Stream h = (ByteStream h) => UTF8Stream h

instance TextStream (UTF8Stream h) ...

addUTF8Encoding :: h -> (UTF8Stream h)

and so on. currently i should add type constraint to each and every
class and function i declared. i don't tried using existential types
here, so i'm not sure that they will give the same high speed for
inlined functions. moreover, they "lose" information about other type
classes that 'h' supports, although functions from these classes may
be required for application that use "UTF8Stream h" and even by other
definitions in my lib:

instance (ByteStringStream h) => ByteStringStream (UTF8Stream h) ...


--
Best regards,
 Bulat                            mailto:[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: newbie type signature question

J. Garrett Morris
In reply to this post by Brandon Moore
On 6/9/06, Brandon Moore <[hidden email]> wrote:
> data DataType m = forall m' . (Monad m') => DataType (TyEq m m') (Char
> -> m' ())

It appears that the more intuitive formulation:

data DataType m
   where DataType :: Monad m => (Char -> m ()) -> DataType m

should work in GHC 6.4

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

RE: newbie type signature question

Brock Peabody
In reply to this post by Brian Hulley
> Brian Hulley wrote:

>   "Don't put class constraints on a data type,
>    constraints belong only to the functions
>    that manipulate the data."
>
> So according to this guideline you're not supposed to think of
associating
> contraints with data: constraints are only relevant for functions
which
> manipulate the data, therefore (if you agree with this view) the very
idea
> of associating constraints with data is wrong.

I wonder what the reasoning for this guideline is.  It seems that it
would require one to repeat the constraint over and over.  For the
Prelude Data.Map type, for instance, wouldn't almost every function have
to have the constraint that the key type is ordered?

Also, I think it would push detection of the error further from the
place where it could have been detected.  Doesn't it make more sense to
detect that there is an error when a user tries to instantiate a map
with an unordered type rather than later when an attempt is made to use
it?


Regards,
Brock

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

RE: Re[2]: newbie type signature question

Brock Peabody
In reply to this post by Bulat Ziganshin-2
> From: Bulat Ziganshin [mailto:[hidden email]]

> when you work with C++ or some other OOP language, you can define that
> some field in structure should some some specific interface and this
> allows to use functions of this interface on this field. i required
> the same feature in Haskell, for example:

I come from a C++ background, and there is no formal way in the language
to specify constraints for type (template) parameters; a major weakness
for generic programming.  Some interesting work is being done to add
this ability though, ex:
http://www.boost.org/libs/concept_check/concept_check.htm.


Regards,
Brock

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

RE: newbie type signature question

Simon Peyton Jones
In reply to this post by Brock Peabody
you need at least one constructor if you say 'where'.  

S

| -----Original Message-----
| From: [hidden email]
[mailto:[hidden email]] On Behalf Of Brock
| Peabody
| Sent: 09 June 2006 20:34
| To: [hidden email]
| Subject: RE: [Haskell-cafe] newbie type signature question
|
| > From: Brandon Moore
|
| > Getting them both is tricky, but you can do it if you use a GADT to
| > write a type that means "exists a such that a = m and a is a Monad":
|
| Is GADT a way to assemble types at compile-time?  It looks really
cool.
|
| > {-# OPTIONS -fglasgow-exts #-}
| > data TyEq (a :: * -> *) (b :: * -> *) where
|                   ^
|                   ^
|
| Compiling this fails here (the first '*') for me with "parse error on
| input '*'" (ghc 6.4.1), but I'll keep playing with it.
|
| Thanks,
|
| Brock
|
|
| _______________________________________________
| 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[2]: newbie type signature question

Brian Hulley
In reply to this post by Bulat Ziganshin-2
Bulat Ziganshin wrote:

> Hello Brian,
>
> Saturday, June 10, 2006, 3:05:25 AM, you wrote:
>
>> It is possible that this feature was added to the language for the
>> benefit of people who prefer not to use explicit type signatures but
>> afaiu this goes against best practice where everything should always
>> have an explicit signature to make code easy to understand and
>> facilitate debugging of type errors.
>
> when you work with C++ or some other OOP language, you can define that
> some field in structure should some some specific interface and this
> allows to use functions of this interface on this field. i required
> the same feature in Haskell, for example:
>
> data UTF8Stream h = (ByteStream h) => UTF8Stream h
>
> instance TextStream (UTF8Stream h) ...
>
> addUTF8Encoding :: h -> (UTF8Stream h)
>
> and so on. currently i should add type constraint to each and every
> class and function i declared.

Hello Bulat -
Thanks for the example. Afaiu, if you don't write any type signature at all,
type inference will infer the constraint, at least this is what I understand
from http://haskell.org/onlinereport/decls.html section 4.2.1:

    data Eq a => Set a = NilSet | ConsSet a (Set a)
    Pattern matching against ConsSet also gives
    rise to an Eq a constraint. For example:

          f (ConsSet a s) = a

    the function f has inferred type Eq a => Set a -> a.

At the moment when you have an explicit type signature you can see exactly
what the function needs, so that if you later refactored the code you could
see which functions actually used the Eq a constraint and which ones didn't.
For example a function to find the size of a set probably wouldn't need Eq
a.

So if the compiler was allowed to add a context to an explicit type
signature you would no longer be able to just look at the type signature for
a function to find out what it was making use of - you'd need to also look
at all the data declarations and gather all the constraints together, to
arrive at the full type signature.

Nevertheless I suppose it would be useful to have some kind of compiler flag
to allow this - certainly given that contexts are allowed on data
declarations it is kind of awkward having to "repeat" all the info on each
function that uses that type, since at the moment the only way to avoid this
repetition is to avoid giving functions that use that type any type
signatures at all, which seems worse.

Or perhaps there could be a special syntax to indicate a partial type
signature, that the compiler would complete by adding the contexts eg:

      f ::: Set a -> a
        ^^^ ? 3 colons to indicate that the compiler will add the relevant
context(s)

> i don't tried using existential types
> here, so i'm not sure that they will give the same high speed for
> inlined functions. moreover, they "lose" information about other type
> classes that 'h' supports, although functions from these classes may
> be required for application that use "UTF8Stream h" and even by other
> definitions in my lib:
>
> instance (ByteStringStream h) => ByteStringStream (UTF8Stream h) ...

Yes I also think they'd be slower and not recommended unless they're
actually needed eg for getting a common interface to objects whose concrete
types aren't known at compile time (being like virtual functions in C++). I
just mentioned them as an example of something not relevant to the
guideline. (Whoever wrote the guideline might have had more reasons but
that's all I can think up so far.)

Best regards, Brian.

--
Logic empowers us and Love gives us purpose.
Yet still phantoms restless for eras long past,
congealed in the present in unthought forms,
strive mightily unseen to destroy us.

http://www.metamilk.com 

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

Re[4]: newbie type signature question

Bulat Ziganshin-2
Hello Brian,

Tuesday, June 13, 2006, 1:11:37 AM, you wrote:

>> data UTF8Stream h = (ByteStream h) => UTF8Stream h
>>
>> instance TextStream (UTF8Stream h) ...
>>
>> addUTF8Encoding :: h -> (UTF8Stream h)
>>
>> and so on. currently i should add type constraint to each and every
>> class and function i declared.

> Hello Bulat -
> Thanks for the example. Afaiu, if you don't write any type signature at all,
> type inference will infer the constraint,

of course, but

1) it is considered as bad programming style
2) haddock currently can't infer function types
3) it doesn't work for class headers
4) functions that create UTF8Stream don't use ByteStream functions

> Or perhaps there could be a special syntax to indicate a partial type
> signature, that the compiler would complete by adding the contexts eg:

>       f ::: Set a -> a
>         ^^^ ? 3 colons to indicate that the compiler will add the relevant
> context(s)

partial signatures is also among the proposals for future Haskell, and
Oleg has technique to make partial signatures even in H98

>> i don't tried using existential types

> Yes I also think they'd be slower

Simon Marlow once said it can be faster. i don't know details, though.
something like the dictionaries for my types are constructed on each
call (?), while dictionaries for existential type constructed only
once, on the moment of encapsulation



--
Best regards,
 Bulat                            mailto:[hidden email]

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