Proposal: Don't require users to use undefined

Dear all, Users of the sizeOf or alignment methods (:: a -> Int) of the Storable class are pushed to use 'undefined' in their programs. Take the following function from Foreign.Marshal.Alloc as an example: malloc :: Storable a => IO (Ptr a) malloc = doMalloc undefined where doMalloc :: Storable b => b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy) I find the use of 'undefined' ugly; its only purpose is to help the type-checker by carrying around a type variable. It also makes the job of an optimizing compiler harder because, in order to avoid generating unnecessary code, the compiler needs to find out if undefined isn't used. More importantly however, 'undefined' is dangerous; The type-checker will never complain when you accidentally use 'undefined' in the wrong place increasing the change that your program will crash. Also, instance writers for the Storable class need to be careful not to evaluate the argument of sizeOf because it may be undefined. The use of 'undefined' is not only required by the Storable class. Users of the HasResolution class from Data.Fixed also need to use 'undefined' in order to get the resolution of a fixed value: class HasResolution a where resolution :: p a -> Integer I would like to propose solving this. My proposal consists of 3 sub-proposals: 1) Add module Data.Tagged. 2) Modify the sizeOf and alignment methods of the Storable class. 3) Modify the HasResolution class. What follows are more detailed explanations of the proposals: 1) Add module Data.Tagged. My proposal is to move the Data.Tagged module from Edward A. Kmett's tagged package to base. See: http://hackage.haskell.org/package/tagged The only modification that needs to be done is to drop the Default instance for Proxy because this will otherwise require that Data.Default be moved to base as well which isn't my intention. When this proposal is accepted Data.Default can provide the instance instead. 2) Modify the sizeOf and alignment methods of the Storable class. I would like to replace the following: class Storable a where sizeOf :: a -> Int alignment :: a -> Int with: class Storable a where sizeOf :: SizeOf a alignment :: Alignment a type SizeOf a = Tagged a Int type Alignment a = Tagged a Int To retrieve the actual size of type 'a' use: untag (sizeOf :: SizeOf a) where: untag :: Tagged s b -> b from Data.Tagged. See the following for the haddock documentation: http://code.haskell.org/~basvandijk/doc/ghc/html/libraries/base-4.3.0.0/Fore... Here's the definition of the previous malloc function to give you an impression how code looks when my proposals are accepted: malloc :: forall a. Storable a => IO (Ptr a) malloc = mallocBytes (untag (sizeOf :: SizeOf a)) (Note that this does require the ScopedTypeVariables language extension.) 3) Modify the HasResolution class. I would like to modify the HasResolution class in the same way. So replacing: class HasResolution a where resolution :: p a -> Integer with: class HasResolution a where resolution :: Resolution a type Resolution a = Tagged a Integer See the following for the haddock documentation: http://code.haskell.org/~basvandijk/doc/ghc/html/libraries/base-4.3.0.0/Data... Note that Fixed also gets a HasResolution instance: instance HasResolution a => HasResolution (Fixed a) where resolution = retag (resolution :: Resolution a) where: retag :: Tagged s b -> Tagged t b from Data.Tagged. There's a possible 4th proposal that I'm thinking about: The Bits class from Data.Bits has the bitSize :: a -> Int method. Maybe it would be a good idea to replace that as well with: bitSize :: BitSize a; type BitSize a = Tagged a Int However I think bitSize is more often applied to an actual value than to 'undefined' so I need to investigate that a little further. A patch for the base package is attached to the ticket. I also attached patches for the ghc compiler, bytestring, binary, vector and dph. The latter are just some packages that were inside my ghc repository. This email is CCed to the maintainers of these packages. Deadline: 3 weeks from now (Tuesday 16 November 2010) but I will shift it when the discussion demands it. Ticket: http://hackage.haskell.org/trac/ghc/ticket/4443 Regards, Bas

On 26/10/2010, at 17:15, Bas van Dijk wrote:
malloc :: Storable a => IO (Ptr a) malloc = doMalloc undefined where doMalloc :: Storable b => b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy)
I find the use of 'undefined' ugly;
How about: malloc :: Storable a => IO (Ptr a) malloc = doMalloc Nothing where doMalloc :: Storable b => Maybe b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy) or perhaps data Dummy a = Dummy malloc :: Storable a => IO (Ptr a) malloc = doMalloc Dummy where doMalloc :: Storable b => Dummy b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy) Roman

On Tue, Oct 26, 2010 at 8:06 PM, Roman Leshchinskiy
On 26/10/2010, at 17:15, Bas van Dijk wrote:
malloc :: Storable a => IO (Ptr a) malloc = doMalloc undefined where doMalloc :: Storable b => b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy)
I find the use of 'undefined' ugly;
How about:
malloc :: Storable a => IO (Ptr a) malloc = doMalloc Nothing where doMalloc :: Storable b => Maybe b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy)
I don't think this will work because 'Maybe a' doesn't have a Storable instance.
or perhaps
data Dummy a = Dummy
malloc :: Storable a => IO (Ptr a) malloc = doMalloc Dummy where doMalloc :: Storable b => Dummy b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy)
Again I think this won't work because you need to add a Storable instance like: instance Storable a => Storable (Dummy a) where sizeOf _ = sizeOf (undefined :: a) peek = ... poke = ... ... Regards, Bas

On 26/10/2010, at 19:36, Bas van Dijk wrote:
How about:
malloc :: Storable a => IO (Ptr a) malloc = doMalloc Nothing where doMalloc :: Storable b => Maybe b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy)
I don't think this will work because 'Maybe a' doesn't have a Storable instance.
Meh, it's late, just ignore me. This is what I was thinking while typing the above nonsense. You could redefine sizeOf and friends to take (Dummy a) instead of `a' as an argument and then use this: data Dummy a = Dummy malloc :: Storable a => IO (Ptr a) malloc = doMalloc Dummy where doMalloc :: Storable b => Dummy b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy) In any case, for me this is not a compelling reason to break several widely used interfaces. Roman

On Tue, Oct 26, 2010 at 9:36 PM, Roman Leshchinskiy
This is what I was thinking while typing the above nonsense. You could redefine sizeOf and friends to take (Dummy a) instead of `a' as an argument and then use this:
data Dummy a = Dummy
malloc :: Storable a => IO (Ptr a) malloc = doMalloc Dummy where doMalloc :: Storable b => Dummy b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy)
Yes, this is another possibility. It doesn't require undefined but does require a dummy argument which is only used to help the type checker.
In any case, for me this is not a compelling reason to break several widely used interfaces.
Fair enough. However I find the following much easier to read (no auxiliary function and no dummy argument): malloc :: forall a. Storable a => IO (Ptr a) malloc = mallocBytes (untag (sizeOf :: SizeOf a)) Bas

There is a Proxy data type in the Data.Tagged library for exactly this
purpose, with conversions to and from Tagged. Proxy is easier to use with
type annotations, but Tagged seems to be easier for the compiler to erase,
since a Proxy is still an argument.
A less controversial proposal might be to see if it makes sense to just do
the portions of this proposal suited to standardizing Data.Tagged, (removing
the Data.Default dependency) even if the existing library functions are not
converted.
-Edward
On Tue, Oct 26, 2010 at 3:36 PM, Roman Leshchinskiy
On 26/10/2010, at 19:36, Bas van Dijk wrote:
How about:
malloc :: Storable a => IO (Ptr a) malloc = doMalloc Nothing where doMalloc :: Storable b => Maybe b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy)
I don't think this will work because 'Maybe a' doesn't have a Storable instance.
Meh, it's late, just ignore me.
This is what I was thinking while typing the above nonsense. You could redefine sizeOf and friends to take (Dummy a) instead of `a' as an argument and then use this:
data Dummy a = Dummy
malloc :: Storable a => IO (Ptr a) malloc = doMalloc Dummy where doMalloc :: Storable b => Dummy b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy)
In any case, for me this is not a compelling reason to break several widely used interfaces.
Roman

On Tue, Oct 26, 2010 at 8:12 PM, Bulat Ziganshin
I find the use of 'undefined' ugly
yu aren't groked lazy evaluation :)
I know that 'sizeOf (undefined :: Word8)' will never evaluate the 'undefined' due to laziness and is completely safe. However, my point is that using 'undefined': * is dangerous because the type checker won't warn you when you accidentally put 'undefined' in the wrong place, * is dangerous when you accidentally evaluate the argument in an instance of Storable, * is harder to optimize because of the unused argument, * is only used for type-checking and so why not help the type-checker by only using a type (Tagged a Int). Also note that the patches attached to the ticket simplify a lot of code. See that in most files there are more '-'s than '+'s: http://hackage.haskell.org/trac/ghc/attachment/ticket/4443/base_ticket_4443.... (To be fair, most of these functions could have been written without using helper functions by using 'forall a. ... sizeOf (undefined :: a)' directly.) Regards, Bas

Bas van Dijk schrieb:
On Tue, Oct 26, 2010 at 8:12 PM, Bulat Ziganshin
wrote: I find the use of 'undefined' ugly yu aren't groked lazy evaluation :)
I know that 'sizeOf (undefined :: Word8)' will never evaluate the 'undefined' due to laziness and is completely safe.
It's pretty easy to be too strict accidentally: sizeOf (a,b) = sizeOf a + sizeOf b Instead you have to write sizeOf ~(a,b) = sizeOf a + sizeOf b (Replace (,) with any other data constructor.)

I just realized that this proposal depends heavily on the ScopedTypeVariables language extension. This could be problematic. Are ScopedTypeVariables in Haskell 2010? If not, are there any plans of putting it in Haskell 2011? Regards, Bas

On Tue, 26 Oct 2010, Bas van Dijk wrote:
malloc :: forall a. Storable a => IO (Ptr a) malloc = mallocBytes (untag (sizeOf :: SizeOf a))
(Note that this does require the ScopedTypeVariables language extension.)
Haskell 98 solution would be nicer. Something like
malloc :: Storable a => IO (Ptr a) malloc = let aux :: Storable a => SizeOf a -> IO (Ptr a) aux = mallocBytes . untag in aux sizeOf
There's a possible 4th proposal that I'm thinking about: The Bits class from Data.Bits has the bitSize :: a -> Int method. Maybe it would be a good idea to replace that as well with: bitSize :: BitSize a; type BitSize a = Tagged a Int However I think bitSize is more often applied to an actual value than to 'undefined' so I need to investigate that a little further.
RealFloat class also provides a lot of examples that could be adapted to the Tagged scheme. Since all of the affected modules are quite basic, this would break a lot of code. Thus I would propose a less invasive path: 1. Add functions that provide the Tagged interface and implement them using 'sizeOf' and 'alignment'. 2. Add functions that convert a sizeOfTagged and alignmentTagged function to 'sizeOf' and 'alignment' in order to allow Storable instance declaration, that not need to cope with undefined. 3. Document how to use the Tagged types with Haskell 98. 4. In the future you may deprecate the immediate use of 'sizeOf' and 'alignment'.

On Tue, Oct 26, 2010 at 9:09 PM, Henning Thielemann
On Tue, 26 Oct 2010, Bas van Dijk wrote:
malloc :: forall a. Storable a => IO (Ptr a) malloc = mallocBytes (untag (sizeOf :: SizeOf a))
(Note that this does require the ScopedTypeVariables language extension.)
Haskell 98 solution would be nicer. Something like
malloc :: Storable a => IO (Ptr a) malloc =
let aux :: Storable a => SizeOf a -> IO (Ptr a) aux = mallocBytes . untag in aux sizeOf
Great! It's nice to know that this proposal doesn't require ScopedTypeVariables.
There's a possible 4th proposal that I'm thinking about: The Bits class from Data.Bits has the bitSize :: a -> Int method. Maybe it would be a good idea to replace that as well with: bitSize :: BitSize a; type BitSize a = Tagged a Int However I think bitSize is more often applied to an actual value than to 'undefined' so I need to investigate that a little further.
RealFloat class also provides a lot of examples that could be adapted to the Tagged scheme.
I see if I can include them in this proposal as well.
Since all of the affected modules are quite basic, this would break a lot of code. Thus I would propose a less invasive path:
1. Add functions that provide the Tagged interface and implement them using 'sizeOf' and 'alignment'.
2. Add functions that convert a sizeOfTagged and alignmentTagged function to 'sizeOf' and 'alignment' in order to allow Storable instance declaration, that not need to cope with undefined.
I agree that we should provide a comfortable upgrade path. With respect to 1 and 2, what about putting them inside the Storable class and defining them in terms of eachother: -- | Minimal complete definition: either sizeOf or sizeOfTagged, ... class Storable a where {-# DEPRECATE sizeOf "use sizeOfTagged instead" #-} sizeOf :: a -> Int sizeOf _ = untag (sizeOfTagged :: SizeOf a) sizeOfTagged :: SizeOf a sizeOfTagged = Tagged $ sizeOf (undefined :: a) {-# DEPRECATE alignment "use alignmentTagged instead" #-} alignment :: a -> Int alignment _ = untag (alignmentTagged :: SizeOf a) alignmentTagged :: SizeOf a alignmentTagged = Tagged $ alignment (undefined :: a) I don't like the name 'sizeOfTagged' though. The name I like best is just 'size :: SizeOf a'. However this is not going to work for 'alignmentTagged' because we already have 'alignment'. Any other ideas?
3. Document how to use the Tagged types with Haskell 98.
Will do. Thanks for the example.
4. In the future you may deprecate the immediate use of 'sizeOf' and 'alignment'.
We could deprecate sizeOf immediately in favor of sizeOfTagged. Deprecating it doesn't break any code but does gives warnings so that users are encouraged to upgrade. I don't see a reason to wait. Thanks, Bas

On Wed, Oct 27, 2010 at 7:51 AM, Bas van Dijk
On Tue, Oct 26, 2010 at 9:09 PM, Henning Thielemann
wrote: 4. In the future you may deprecate the immediate use of 'sizeOf' and 'alignment'.
We could deprecate sizeOf immediately in favor of sizeOfTagged. Deprecating it doesn't break any code but does gives warnings so that users are encouraged to upgrade. I don't see a reason to wait.
We can't immediately switch to using sizeOfTagged since many people will still be using old versions of base. If we waited one cycle to deprecate the old functions, it won't annoy people. By the next cycle, in theory a majority of people will already have sizeOfTagged, and it would be feasible to switch our code to sizeOf. Michael

On Wed, Oct 27, 2010 at 7:57 AM, Michael Snoyman
On Wed, Oct 27, 2010 at 7:51 AM, Bas van Dijk
wrote: On Tue, Oct 26, 2010 at 9:09 PM, Henning Thielemann
wrote: 4. In the future you may deprecate the immediate use of 'sizeOf' and 'alignment'.
We could deprecate sizeOf immediately in favor of sizeOfTagged. Deprecating it doesn't break any code but does gives warnings so that users are encouraged to upgrade. I don't see a reason to wait.
We can't immediately switch to using sizeOfTagged since many people will still be using old versions of base. If we waited one cycle to deprecate the old functions, it won't annoy people. By the next cycle, in theory a majority of people will already have sizeOfTagged, and it would be feasible to switch our code to sizeOf.
Yes, that's what I meant. We should {-# DEPRECATE #-} sizeOf now and provide sizeOfTagged as an alternative. In a further cycle we can remove sizeOf.

On Wed, Oct 27, 2010 at 9:01 AM, Bas van Dijk
On Wed, Oct 27, 2010 at 7:57 AM, Michael Snoyman
wrote: On Wed, Oct 27, 2010 at 7:51 AM, Bas van Dijk
wrote: On Tue, Oct 26, 2010 at 9:09 PM, Henning Thielemann
wrote: 4. In the future you may deprecate the immediate use of 'sizeOf' and 'alignment'.
We could deprecate sizeOf immediately in favor of sizeOfTagged. Deprecating it doesn't break any code but does gives warnings so that users are encouraged to upgrade. I don't see a reason to wait.
We can't immediately switch to using sizeOfTagged since many people will still be using old versions of base. If we waited one cycle to deprecate the old functions, it won't annoy people. By the next cycle, in theory a majority of people will already have sizeOfTagged, and it would be feasible to switch our code to sizeOf.
Yes, that's what I meant. We should {-# DEPRECATE #-} sizeOf now and provide sizeOfTagged as an alternative. In a further cycle we can remove sizeOf.
My point is that if you use DEPRECATE immediately, no one will be able to compile with -Wall -Werror. No one can reasonably switch over to sizeOfTagged until at least the next cycle, and by putting in a deprecation immediately, we'll start generating warnings immediately for writing code in the only reasonable way possible. There is of course the possibility of conditional compilation, but I like to avoid that whenever possible. Michael

On Wed, Oct 27, 2010 at 9:15 AM, Michael Snoyman
On Wed, Oct 27, 2010 at 9:01 AM, Bas van Dijk
wrote: On Wed, Oct 27, 2010 at 7:57 AM, Michael Snoyman
wrote: On Wed, Oct 27, 2010 at 7:51 AM, Bas van Dijk
wrote: On Tue, Oct 26, 2010 at 9:09 PM, Henning Thielemann
wrote: 4. In the future you may deprecate the immediate use of 'sizeOf' and 'alignment'.
We could deprecate sizeOf immediately in favor of sizeOfTagged. Deprecating it doesn't break any code but does gives warnings so that users are encouraged to upgrade. I don't see a reason to wait.
We can't immediately switch to using sizeOfTagged since many people will still be using old versions of base. If we waited one cycle to deprecate the old functions, it won't annoy people. By the next cycle, in theory a majority of people will already have sizeOfTagged, and it would be feasible to switch our code to sizeOf.
Yes, that's what I meant. We should {-# DEPRECATE #-} sizeOf now and provide sizeOfTagged as an alternative. In a further cycle we can remove sizeOf.
My point is that if you use DEPRECATE immediately, no one will be able to compile with -Wall -Werror. No one can reasonably switch over to sizeOfTagged until at least the next cycle, and by putting in a deprecation immediately, we'll start generating warnings immediately for writing code in the only reasonable way possible.
Ok I get your point. Then we could at least put a note in the documentation that sizeOf is soon to be deprecated in favor of sizeOfTagged. (BTW Note that base-4.3 will also be deprecating block and unblock in favor of mask. Maybe that should follow this procedure as well.) Bas

Hi all, On 27.10.2010, at 09:01, Bas van Dijk wrote:
On Wed, Oct 27, 2010 at 7:57 AM, Michael Snoyman
wrote: On Wed, Oct 27, 2010 at 7:51 AM, Bas van Dijk
wrote: On Tue, Oct 26, 2010 at 9:09 PM, Henning Thielemann
wrote: 4. In the future you may deprecate the immediate use of 'sizeOf' and 'alignment'.
We could deprecate sizeOf immediately in favor of sizeOfTagged. Deprecating it doesn't break any code but does gives warnings so that users are encouraged to upgrade. I don't see a reason to wait.
We can't immediately switch to using sizeOfTagged since many people will still be using old versions of base. If we waited one cycle to deprecate the old functions, it won't annoy people. By the next cycle, in theory a majority of people will already have sizeOfTagged, and it would be feasible to switch our code to sizeOf.
Yes, that's what I meant. We should {-# DEPRECATE #-} sizeOf now and provide sizeOfTagged as an alternative. In a further cycle we can remove sizeOf.
I think this whole proposal is a bad idea. There is a cost-benefit tradeoff in every library change. I do not see the problem that anybody is every going to accidentally evaluate sizeOf or alginment and get an unexpected "undefined" value back. I don't see the compiler optimization problem - it may exists but if the argument of Storage is fixed (and it always is in any use of Storage) then the compiler should be able to inline the 4 or 8 or whatever alignment evaluates to. So the only benefit I see is that code becomes slightly prettier. But it is not code that is read a lot, it's boilerplate code that is used internally. No sane Haskell library that interfaces to C exports Ptr values and expects the user to use sizeOf and alignment to operate on the C data structure. Instead C data structures are accessed using a Haskell interface. So the danger of library users calling sizeOf and getting an "undefined" is really zero. Even as a library maintainer, I've never managed to evaluate sizeOf by accident. In Gtk2Hs, we create a lot of Storable instances. If we were to implement the proposed change, we would have to manually edit a lot of code. There is no benefit for the user. None. There is only a lot of work. For future code, the benefit is also near to zero since, from my past experience, the problems that motivated this proposal do not exist in practice. Cheers, Axel

On 10/27/10 01:51, Bas van Dijk wrote:
On Tue, Oct 26, 2010 at 9:09 PM, Henning Thielemann
wrote: On Tue, 26 Oct 2010, Bas van Dijk wrote:
malloc :: forall a. Storable a => IO (Ptr a) malloc = mallocBytes (untag (sizeOf :: SizeOf a))
(Note that this does require the ScopedTypeVariables language extension.)
Haskell 98 solution would be nicer. Something like
malloc :: Storable a => IO (Ptr a) malloc =
let aux :: Storable a => SizeOf a -> IO (Ptr a) aux = mallocBytes . untag in aux sizeOf
Great! It's nice to know that this proposal doesn't require ScopedTypeVariables.
It may not *require* it, but the code using ScopedTypeVariables is far more readable than that Haskell98/Haskell2010 code (though, perhaps we can improve on its readability a bit). (ScopedTypeVariables is not in Haskell2010, according to http://www.haskell.org/onlinereport/haskell2010/haskellli2.html#x3-5000 ) -Isaac

On Wed, Oct 27, 2010 at 8:09 AM, Isaac Dupree
On 10/27/10 01:51, Bas van Dijk wrote:
On Tue, Oct 26, 2010 at 9:09 PM, Henning Thielemann
wrote: On Tue, 26 Oct 2010, Bas van Dijk wrote:
malloc :: forall a. Storable a => IO (Ptr a) malloc = mallocBytes (untag (sizeOf :: SizeOf a))
(Note that this does require the ScopedTypeVariables language extension.)
Haskell 98 solution would be nicer. Something like
malloc :: Storable a => IO (Ptr a) malloc =
let aux :: Storable a => SizeOf a -> IO (Ptr a) aux = mallocBytes . untag in aux sizeOf
Great! It's nice to know that this proposal doesn't require ScopedTypeVariables.
It may not *require* it, but the code using ScopedTypeVariables is far more readable than that Haskell98/Haskell2010 code (though, perhaps we can improve on its readability a bit).
Indeed. My patches for base and the other packages uses ScopedTypeVariables extensively. It makes code shorter and easier to read. Is using ScopedTypeVariables a problem by the way? Should base use Haskell2010 exclusively or are extensions allowed?
(ScopedTypeVariables is not in Haskell2010, according to http://www.haskell.org/onlinereport/haskell2010/haskellli2.html#x3-5000 )
I hope they will be in Haskell2011. Bas

Bas van Dijk wrote:
On Wed, Oct 27, 2010 at 8:09 AM, Isaac Dupree
Is using ScopedTypeVariables a problem by the way? Should base use Haskell2010 exclusively or are extensions allowed?
(ScopedTypeVariables is not in Haskell2010, according to
http://www.haskell.org/onlinereport/haskell2010/haskellli2.html#x3-500
0 )
I hope they will be in Haskell2011.
Things don't appear in Haskell2011 by magic :-) In this case there isn't a completed proposal, so they won't be (unless perhaps someone does something very quickly). See http://hackage.haskell.org/trac/haskell-prime/ for an overview of the process. There's already a proposal (http://hackage.haskell.org/trac/haskell-prime/ticket/67) but it's stuck in the "None" bucket (http://hackage.haskell.org/trac/haskell-prime/query?status=new&status=a ssigned&status=reopened&group=state) and hasn't been touched for a long time. It needs discussion and a "Report Delta". Ganesh =============================================================================== Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html ===============================================================================

On 27/10/2010 08:12, Bas van Dijk wrote:
On Wed, Oct 27, 2010 at 8:09 AM, Isaac Dupree
wrote: On 10/27/10 01:51, Bas van Dijk wrote:
On Tue, Oct 26, 2010 at 9:09 PM, Henning Thielemann
wrote: On Tue, 26 Oct 2010, Bas van Dijk wrote:
malloc :: forall a. Storable a => IO (Ptr a) malloc = mallocBytes (untag (sizeOf :: SizeOf a))
(Note that this does require the ScopedTypeVariables language extension.)
Haskell 98 solution would be nicer. Something like
malloc :: Storable a => IO (Ptr a) malloc =
let aux :: Storable a => SizeOf a -> IO (Ptr a) aux = mallocBytes . untag in aux sizeOf
Great! It's nice to know that this proposal doesn't require ScopedTypeVariables.
It may not *require* it, but the code using ScopedTypeVariables is far more readable than that Haskell98/Haskell2010 code (though, perhaps we can improve on its readability a bit).
Indeed. My patches for base and the other packages uses ScopedTypeVariables extensively. It makes code shorter and easier to read.
Note that the original version is shorter and easier to read if you use ScopedTypeVariables too. We deliberately avoided ScopedTypeVariables in that code in order to make it compatible with Haskell 98.
Is using ScopedTypeVariables a problem by the way? Should base use Haskell2010 exclusively or are extensions allowed?
(ScopedTypeVariables is not in Haskell2010, according to http://www.haskell.org/onlinereport/haskell2010/haskellli2.html#x3-5000 )
I hope they will be in Haskell2011.
Highly unlikely. ScopedTypeVariables is a big extension with room for alternative designs, it will take a lot of effort to get it into the language standard. It depends on ExplicitForall for one thing, which still isn't in the language and that's fairly straightforward. Cheers, Simon

Small bit of bikeshed... Isn't Tagged also the so-called strip / stripping functor? a.k.a the opposite of the Const functor. Personally I'd prefer the name Strip. To me, the name Tagged is suggestive of adding something to a data type. I suppose it is adding something to a type constructor, but I'd still vote for Strip as it seems more "twinned" with Const.

| Users of the sizeOf or alignment methods (:: a -> Int) of the Storable | class are pushed to use 'undefined' in their programs. Take the | following function from Foreign.Marshal.Alloc as an example: | | malloc :: Storable a => IO (Ptr a) | malloc = doMalloc undefined | where | doMalloc :: Storable b => b -> IO (Ptr b) | doMalloc dummy = mallocBytes (sizeOf dummy) | ... | | I would like to propose solving this. My proposal consists of 3 sub-proposals: I'm not keen on this proposal. a) The improvement is minor b) A lot of exixting code has to be changed c) There is a better way to do the job What is really going on? We have sizeOf :: forall a. Storable a => a -> Int What we *want* is newSizeOf :: forall a. Storable a => Int but in H98 we call newSizeOf because there would be no way to tell newSizeOf which type to use. The obvious solution is something Haskell badly needs: explicit type applications. Instead of writing sizeOf (undefined :: Int) we should write newSizeOf @ Int where the "@" says that the next argument is a type. As Haskell's type system becomes more sophisticated, the lack of type applications becomes more and more annoying. For example, to get impredicative polymorphism I want to write Just @ (forall a. a->a) id to get something of type Maybe (forall a. a->a) It's only a matter of time before we have explicit type applications. Indeed, the principal difficulty is *syntax*. (My "@" notation above might just work; because "@" is already unavailable as an operator.) Given explicit type applications and scoped type variables, it's easy to do the Right Thing for your examples. And the Right Thing works at higher kinds too. Eg class Typeable1 (a :: *->*) where typeOf1 :: TypeRep Then we can say (typeOf1 @ Maybe). This is harder with proxies; I don't think it's covered by your proposal. Now, none of this is H98, of course. But we already have a H98 solution. I don't really think it's worth the disruption to switch from one workaround to another, even aside from dealing with the long-running deprecation and compatibility issues that then show up. Simon

On Wed, Oct 27, 2010 at 07:43:13AM +0000, Simon Peyton-Jones wrote:
It's only a matter of time before we have explicit type applications. Indeed, the principal difficulty is *syntax*. (My "@" notation above might just work; because "@" is already unavailable as an operator.)
Drifting off-topic, but wouldn't we want to be able to use similar syntax to bind types too? e.g. f ((Just @ t) x) = (Right @ String @ t) x but @ is unavailable in patterns. Thanks Ian

| Drifting off-topic, but wouldn't we want to be able to use similar | syntax to bind types too? e.g. | | f ((Just @ t) x) = (Right @ String @ t) x | | but @ is unavailable in patterns. Oh yes, good point. It'd be particularly useful in existential patterns: data T where MkT :: forall a. a -> (a -> Int) -> T f (MkT @ a x g) = g (x::a) The idea is that the pattern (MkT @ a x g) brings the type variable 'a' into scope. As you point out, though, '@' is already used in patterns, but perhaps this use is unambiguous. Confusing though f (MkS @ a x@(p,q) z) = .... Maybe someone else can think of good syntax. Simon

Simon Peyton-Jones wrote:
Drifting off-topic, but wouldn't we want to be able to use similar syntax to bind types too? e.g.
f ((Just @ t) x) = (Right @ String @ t) x
but @ is unavailable in patterns.
Oh yes, good point. It'd be particularly useful in existential patterns:
data T where MkT :: forall a. a -> (a -> Int) -> T
f (MkT @ a x g) = g (x::a)
The idea is that the pattern (MkT @ a x g) brings the type variable 'a' into scope. As you point out, though, '@' is already used in patterns, but perhaps this use is unambiguous. Confusing though f (MkS @ a x@(p,q) z) = ....
Maybe someone else can think of good syntax.
Couldn't this work? f (MkT (x :: a) g) In theory one could imagine wanting to write data T where MkT :: forall a . Foo a => T and then some special syntax might be needed, but I can't think of any real use for this off the top of my head. But in general the idea of having a whole new syntax for passing types around makes me a bit nervous; GHC Haskell is already getting quite syntactically cluttered. We already have machinery for passing around values and abstracting over them, so if we can use a single well-known proxy value to pass around types we can reuse that machinery. Couldn't the typeOf Maybe problem be solved with polymorphic kinds, which would be nice to have anyway? =============================================================================== Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html ===============================================================================

| Couldn't this work? | | f (MkT (x :: a) g) Yes you can do this today, but it's a bit clumsy and indirect. And it gets harder when there are more complicated constraints data T where MkT :: forall a b c. (F a ~ G b, F b ~ c) => a -> T c I don't know of a practical application, but in the dual case of applications (rather than pattern matching) we have found that specifying type arguments indirectly is sometimes very clumsy. So I like the idea of having a bale-out route to the *direct* thing. By "direct" I mean: the source program desugars to a program with type abstractions, applications and type variables bound in pattern matches. | But in general the idea of having a whole new syntax for passing types | around makes me a bit nervous; GHC Haskell is already getting quite | syntactically cluttered. Yes, but see above. There really is a principled reason for wanting this: it's not just a random feature, it's the ground truth. Type inference "fills in" the missing type arguments most of the time, but just as type signatures are usually redundant but sometimes useful, so are type arguments. It's not a new mechanism: it's just surface syntax for an existing mechanism. Simon

Am 27.10.2010 21:57, schrieb Simon Peyton-Jones:
| Drifting off-topic, but wouldn't we want to be able to use similar | syntax to bind types too? e.g. | | f ((Just @ t) x) = (Right @ String @ t) x | | but @ is unavailable in patterns.
Oh yes, good point. It'd be particularly useful in existential patterns:
data T where MkT :: forall a. a -> (a -> Int) -> T
f (MkT @ a x g) = g (x::a)
The idea is that the pattern (MkT @ a x g) brings the type variable 'a' into scope. As you point out, though, '@' is already used in patterns, but perhaps this use is unambiguous. Confusing though f (MkS @ a x@(p,q) z) = ....
Maybe someone else can think of good syntax.
No, I cannot, but other languages (like pvs, opal, HasCASL with paramterized modules) use optional type arguments in square brackets that would clash/overlap Haskell's list syntax. I.e the function "length" could be instantiated to work on Int-Lists by writing "length[Int]". which would have type [Int] -> Int The problem is to distinguish "length[Int]" (of type [Int] -> Int) from "length[True]" (of type Int) in particular for a data type like data Void = Void and the term "length[Void]". The compiler could report an ambiguity and suggest to write "length[Void :: Void]" or "length[Void :: *]". A type list would also bind stronger to a name than a value list. map length [True] ---> (map length) [True] :: [Int] map length [Bool] ---> map (length [Bool]) :: [[Bool]] -> [Int] The next problem is the order for multiple type variables: map f (x : xs) = f x : map f xs Questions: 1. is an explicit type signature requires map :: (a -> b) -> [a] -> [b] 2. is an explicit "forall a b." needed to fix the order of the type variables? Otherwise the order could be taken from the first occurrences in the type signature (but would "forall b a . a -> b" then be rejected, because the orders are contradictory?). (Surely, the order should not be taken from the variable names like "a" before "b".) An instance of map is written as "map[[Int], Int] length" (of type [[Int]] -> [Int]). Writing "[Ty1, Ty2]" looks better to me than "[Ty1][Ty2]". The operator "@" compared to square brackets has the disadvantage that usually additional parens are needed to separate subsequent arguments: (length @ Bool) [True] (length @ Int) [1] (Otherwise "Bool [True]" would be taken as wrong type.) Also how should "map length @ Int" be parsed? One wants "map (length @ Int)", but other infix operators (even "::") are parsed as "(map length) @ Int". Cheers Christian

On Thu, Oct 28, 2010 at 11:33 AM, Christian Maeder
...other languages (like pvs, opal, HasCASL with paramterized modules) use optional type arguments in square brackets that would clash/overlap Haskell's list syntax. ...
This reminds me of Agda's implicit arguments: See section 2.3 of: http://www.cse.chalmers.se/~ulfn/papers/afp08/tutorial.pdf For example: map : {A B : Set} -> (A -> B) -> List A -> List B map f [] = [] map f (x :: xs) = f x :: map f xs foo : List Char -> List Int foo = map {Char} {Int} Regards, Bas P.S. This topic is drifting wildly offtopic but I like it ;-) (and we have some weeks left for the proposal)

On Wed, Oct 27, 2010 at 07:57:59PM +0000, Simon Peyton-Jones wrote:
| Drifting off-topic, but wouldn't we want to be able to use similar | syntax to bind types too? e.g. | | f ((Just @ t) x) = (Right @ String @ t) x | | but @ is unavailable in patterns.
Oh yes, good point. It'd be particularly useful in existential patterns:
data T where MkT :: forall a. a -> (a -> Int) -> T
f (MkT @ a x g) = g (x::a)
The idea is that the pattern (MkT @ a x g) brings the type variable 'a' into scope. As you point out, though, '@' is already used in patterns, but perhaps this use is unambiguous. Confusing though f (MkS @ a x@(p,q) z) = ....
Maybe someone else can think of good syntax.
The difference in what "Just" means in (Just 'c') and ((Just @ Char) 'c') feels a bit wrong to me. Maybe it would be a better to have a syntax to get something with its real type, and then use normal application, e.g. currently we have Just :: forall a . a -> Maybe a Just :: Char -> Maybe Char And if #... is the new syntax then: #Just :: /\ a . a -> Maybe a #Just Char :: Char -> Maybe Char Just 'a' == #Just Char 'a' and also map ord "foo" == #map Char Int ord "foo" Thanks Ian

On Fri, Oct 29, 2010 at 4:09 PM, Ian Lynagh
On Wed, Oct 27, 2010 at 07:57:59PM +0000, Simon Peyton-Jones wrote:
| Drifting off-topic, but wouldn't we want to be able to use similar | syntax to bind types too? e.g. | | f ((Just @ t) x) = (Right @ String @ t) x | | but @ is unavailable in patterns.
Oh yes, good point. It'd be particularly useful in existential patterns:
data T where MkT :: forall a. a -> (a -> Int) -> T
f (MkT @ a x g) = g (x::a)
The idea is that the pattern (MkT @ a x g) brings the type variable 'a' into scope. As you point out, though, '@' is already used in patterns, but perhaps this use is unambiguous. Confusing though f (MkS @ a x@(p,q) z) = ....
Maybe someone else can think of good syntax.
The difference in what "Just" means in (Just 'c') and ((Just @ Char) 'c') feels a bit wrong to me.
Maybe it would be a better to have a syntax to get something with its real type, and then use normal application, e.g. currently we have
Just :: forall a . a -> Maybe a Just :: Char -> Maybe Char
And if #... is the new syntax then:
#Just :: /\ a . a -> Maybe a #Just Char :: Char -> Maybe Char
Just 'a' == #Just Char 'a'
This is similar to how Coq does it. If you have a form with implicit arguments (in our case type arguments), you can add a '@' prefix to make them explicit. So you'd write @Just Char 'a'. If we are doing explicit type variables, we might also want to allow _ to mean "infer this argument anyway". That way, when using something like map: map :: forall a b. (a -> b) -> [a] -> [b] you could write: @map Int _ show to let Haskell infer the second type parameter.
and also
map ord "foo" == #map Char Int ord "foo"
I guess this would only work with explicit foralls, otherwise how do
you know which type is the Int and which is the Char?
That is, "forall a b. ..." is different from "forall b a. ...".
--
Dave Menendez

On Fri, Oct 29, 2010 at 05:25:15PM -0400, David Menendez wrote:
map ord "foo" == #map Char Int ord "foo"
I guess this would only work with explicit foralls, otherwise how do you know which type is the Int and which is the Char?
You can do it based on the order they first appear in the type. It would be nicer if explicit foralls were used, but requiring that would mean you can't use this syntax with most library code (e.g., you can't use it with map), as it doesn't currently use them. I don't know how much of a problem this would cause. Thanks Ian

On Fri, Oct 29, 2010 at 10:41:38PM +0100, Ian Lynagh wrote:
On Fri, Oct 29, 2010 at 05:25:15PM -0400, David Menendez wrote:
map ord "foo" == #map Char Int ord "foo"
I guess this would only work with explicit foralls, otherwise how do you know which type is the Int and which is the Char?
You can do it based on the order they first appear in the type.
Actually, maybe it's not so simple once MPTCs, FDs and ATs are mixed in. But if nothing else, requiring explicit foralls would work, anyway. Thanks Ian

On Fri, Oct 29, 2010 at 5:43 PM, Ian Lynagh
On Fri, Oct 29, 2010 at 10:41:38PM +0100, Ian Lynagh wrote:
On Fri, Oct 29, 2010 at 05:25:15PM -0400, David Menendez wrote:
map ord "foo" == #map Char Int ord "foo"
I guess this would only work with explicit foralls, otherwise how do you know which type is the Int and which is the Char?
You can do it based on the order they first appear in the type.
Actually, maybe it's not so simple once MPTCs, FDs and ATs are mixed in.
But if nothing else, requiring explicit foralls would work, anyway.
There's also alphabetical order.
What about stuff like foo :: Int -> forall b. b -> b? Currently, that
gets treated as foo :: forall b. Int -> b -> b, but maybe that's not
desirable with explicit type arguments. (I guess that's an argument
for having a different syntax for type application.)
--
Dave Menendez

On 10/27/10 3:43 AM, Simon Peyton-Jones wrote:
I'm not keen on this proposal. a) The improvement is minor b) A lot of exixting code has to be changed c) There is a better way to do the job
[...] What we *want* is newSizeOf :: forall a. Storable a => Int [...] The obvious solution is something Haskell badly needs: explicit type applications. Instead of writing sizeOf (undefined :: Int) we should write newSizeOf @ Int where the "@" says that the next argument is a type. As Haskell's type system becomes more sophisticated, the lack of type applications becomes more and more annoying. [...] Given explicit type applications and scoped type variables, it's easy to do the Right Thing for your examples. And the Right Thing works at higher kinds too. Eg
class Typeable1 (a :: *->*) where typeOf1 :: TypeRep
Then we can say (typeOf1 @ Maybe). This is harder with proxies; I don't think it's covered by your proposal.
Now, none of this is H98, of course. But we already have a H98 solution. I don't really think it's worth the disruption to switch from one workaround to another, even aside from dealing with the long-running deprecation and compatibility issues that then show up.
+1. -- Live well, ~wren

On 27/10/2010 08:43, Simon Peyton-Jones wrote:
| Users of the sizeOf or alignment methods (:: a -> Int) of the Storable | class are pushed to use 'undefined' in their programs. Take the | following function from Foreign.Marshal.Alloc as an example: | | malloc :: Storable a => IO (Ptr a) | malloc = doMalloc undefined | where | doMalloc :: Storable b => b -> IO (Ptr b) | doMalloc dummy = mallocBytes (sizeOf dummy) | ... | | I would like to propose solving this. My proposal consists of 3 sub-proposals:
I'm not keen on this proposal. a) The improvement is minor b) A lot of exixting code has to be changed c) There is a better way to do the job
Agree. Simon

+1
I'm more than happy to strip the Data.Default instance from Data.Tagged,
I've already had a couple of users complain about how 'heavy' a dependency
it is, since Data.Default has to provide all the definitions for lots of
other libraries that predate it, including the MTL.
-Edward
On Tue, Oct 26, 2010 at 12:15 PM, Bas van Dijk
Dear all,
Users of the sizeOf or alignment methods (:: a -> Int) of the Storable class are pushed to use 'undefined' in their programs. Take the following function from Foreign.Marshal.Alloc as an example:
malloc :: Storable a => IO (Ptr a) malloc = doMalloc undefined where doMalloc :: Storable b => b -> IO (Ptr b) doMalloc dummy = mallocBytes (sizeOf dummy)
I find the use of 'undefined' ugly; its only purpose is to help the type-checker by carrying around a type variable. It also makes the job of an optimizing compiler harder because, in order to avoid generating unnecessary code, the compiler needs to find out if undefined isn't used. More importantly however, 'undefined' is dangerous; The type-checker will never complain when you accidentally use 'undefined' in the wrong place increasing the change that your program will crash. Also, instance writers for the Storable class need to be careful not to evaluate the argument of sizeOf because it may be undefined.
The use of 'undefined' is not only required by the Storable class. Users of the HasResolution class from Data.Fixed also need to use 'undefined' in order to get the resolution of a fixed value:
class HasResolution a where resolution :: p a -> Integer
I would like to propose solving this. My proposal consists of 3 sub-proposals:
1) Add module Data.Tagged. 2) Modify the sizeOf and alignment methods of the Storable class. 3) Modify the HasResolution class.
What follows are more detailed explanations of the proposals:
1) Add module Data.Tagged.
My proposal is to move the Data.Tagged module from Edward A. Kmett's tagged package to base. See: http://hackage.haskell.org/package/tagged The only modification that needs to be done is to drop the Default instance for Proxy because this will otherwise require that Data.Default be moved to base as well which isn't my intention. When this proposal is accepted Data.Default can provide the instance instead.
2) Modify the sizeOf and alignment methods of the Storable class.
I would like to replace the following:
class Storable a where sizeOf :: a -> Int alignment :: a -> Int
with:
class Storable a where sizeOf :: SizeOf a alignment :: Alignment a
type SizeOf a = Tagged a Int type Alignment a = Tagged a Int
To retrieve the actual size of type 'a' use: untag (sizeOf :: SizeOf a) where: untag :: Tagged s b -> b from Data.Tagged.
See the following for the haddock documentation:
http://code.haskell.org/~basvandijk/doc/ghc/html/libraries/base-4.3.0.0/Fore...
Here's the definition of the previous malloc function to give you an impression how code looks when my proposals are accepted:
malloc :: forall a. Storable a => IO (Ptr a) malloc = mallocBytes (untag (sizeOf :: SizeOf a))
(Note that this does require the ScopedTypeVariables language extension.)
3) Modify the HasResolution class.
I would like to modify the HasResolution class in the same way. So replacing:
class HasResolution a where resolution :: p a -> Integer
with:
class HasResolution a where resolution :: Resolution a
type Resolution a = Tagged a Integer
See the following for the haddock documentation:
http://code.haskell.org/~basvandijk/doc/ghc/html/libraries/base-4.3.0.0/Data...
Note that Fixed also gets a HasResolution instance:
instance HasResolution a => HasResolution (Fixed a) where resolution = retag (resolution :: Resolution a)
where: retag :: Tagged s b -> Tagged t b from Data.Tagged.
There's a possible 4th proposal that I'm thinking about: The Bits class from Data.Bits has the bitSize :: a -> Int method. Maybe it would be a good idea to replace that as well with: bitSize :: BitSize a; type BitSize a = Tagged a Int However I think bitSize is more often applied to an actual value than to 'undefined' so I need to investigate that a little further.
A patch for the base package is attached to the ticket. I also attached patches for the ghc compiler, bytestring, binary, vector and dph. The latter are just some packages that were inside my ghc repository. This email is CCed to the maintainers of these packages.
Deadline: 3 weeks from now (Tuesday 16 November 2010) but I will shift it when the discussion demands it.
Ticket: http://hackage.haskell.org/trac/ghc/ticket/4443
Regards,
Bas
participants (18)
-
Axel Simon
-
Bas van Dijk
-
Bulat Ziganshin
-
Christian Maeder
-
David Menendez
-
Edward Kmett
-
Henning Thielemann
-
Henning Thielemann
-
Ian Lynagh
-
Isaac Dupree
-
Malcolm Wallace
-
Michael Snoyman
-
Roman Leshchinskiy
-
Simon Marlow
-
Simon Peyton-Jones
-
Sittampalam, Ganesh
-
Stephen Tetley
-
wren ng thornton