From harendra.kumar at gmail.com Wed Jan 5 08:01:00 2022 From: harendra.kumar at gmail.com (Harendra Kumar) Date: Wed, 5 Jan 2022 13:31:00 +0530 Subject: Storable instance of () is broken Message-ID: The Storable instance of () is defined in the "Foreign.Storable" module of the "base" package as follows: instance Storable () where sizeOf _ = 0 alignment _ = 1 peek _ = return () poke _ _ = return () The size of () is defined as 0. It sounds absurd for a Storable to have a size of 0? This means that we can read an infinite number of () type values out of nothing (no memory location required) or store an infinite number of () type values without even requiring a memory location to write to. This is causing a practical problem in our Storable array implementation. The array is constrained to a Storable type. Since () has a Storable instance, one can store () in the Storable array. But it causes a problem because we determine the array element size using sizeOf on the type. For () type it turns out to be 0. Essentially, the array of () would always be of size 0. Now, we cannot determine the length of the array from its byte length as you could store infinite such elements in an empty array. The Storable instance of () seems to be an oddity and makes us use a special case everywhere in the code to handle this, and this special casing makes it highly prone to errors when we change code. Can this be fixed? Is there a compelling argument to keep it like this? A possible fix could be to represent it by a single byte in memory which can be discarded when reading or writing. Another alternative is to not provide a Storable instance for it at all. Let the users write their own if they need it. If you think this does not have a problem, can you suggest how to elegantly handle the array implementation problem as I described above? Thanks, Harendra From david.feuer at gmail.com Wed Jan 5 08:13:46 2022 From: david.feuer at gmail.com (David Feuer) Date: Wed, 5 Jan 2022 03:13:46 -0500 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: I don't think it's broken; I think your length calculation is broken. Instead of asking every use of () to take an extra byte, why don't you just store a word saying how long your array is? Alternatively, you could probably avoid special cases with a newtype: newtype NZS a = NZS { unNZS :: a } instance Storable a => Storable (NZS a) where sizeof | sizeof (undefined @a) == 0 = const 1 | otherwise = sizeof .# unNZS alignment = alignment .# unNZS peekElemOff = coerce (peekElemOff @a) .... On Wed, Jan 5, 2022, 3:01 AM Harendra Kumar wrote: > The Storable instance of () is defined in the "Foreign.Storable" > module of the "base" package as follows: > > instance Storable () where > sizeOf _ = 0 > alignment _ = 1 > peek _ = return () > poke _ _ = return () > > The size of () is defined as 0. It sounds absurd for a Storable to > have a size of 0? This means that we can read an infinite number of () > type values out of nothing (no memory location required) or store an > infinite number of () type values without even requiring a memory > location to write to. > > This is causing a practical problem in our Storable array > implementation. The array is constrained to a Storable type. Since () > has a Storable instance, one can store () in the Storable array. But > it causes a problem because we determine the array element size using > sizeOf on the type. For () type it turns out to be 0. Essentially, the > array of () would always be of size 0. Now, we cannot determine the > length of the array from its byte length as you could store infinite > such elements in an empty array. The Storable instance of () seems to > be an oddity and makes us use a special case everywhere in the code to > handle this, and this special casing makes it highly prone to errors > when we change code. > > Can this be fixed? Is there a compelling argument to keep it like > this? A possible fix could be to represent it by a single byte in > memory which can be discarded when reading or writing. Another > alternative is to not provide a Storable instance for it at all. Let > the users write their own if they need it. > > If you think this does not have a problem, can you suggest how to > elegantly handle the array implementation problem as I described > above? > > Thanks, > Harendra > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From lemming at henning-thielemann.de Wed Jan 5 10:04:55 2022 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Wed, 5 Jan 2022 11:04:55 +0100 (CET) Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: On Wed, 5 Jan 2022, Harendra Kumar wrote: > If you think this does not have a problem, can you suggest how to > elegantly handle the array implementation problem as I described above? In my package comfort-array (e.g. for Storable arrays) I use an array 'shape' to determine the size of the array: https://hackage.haskell.org/package/comfort-array From svenpanne at gmail.com Wed Jan 5 10:13:37 2022 From: svenpanne at gmail.com (Sven Panne) Date: Wed, 5 Jan 2022 11:13:37 +0100 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: Am Mi., 5. Jan. 2022 um 09:01 Uhr schrieb Harendra Kumar < harendra.kumar at gmail.com>: > [...] The size of () is defined as 0. It sounds absurd for a Storable to > have a size of 0? This is not absurd at all, there is absolutely no information to be stored. Everything one needs to know is in the type here. > This means that we can read an infinite number of () > type values out of nothing (no memory location required) or store an > infinite number of () type values without even requiring a memory > location to write to. > Exactly. > [...] Can this be fixed? Is there a compelling argument to keep it like > this? [...] There is nothing to be fixed on the Storable side of things, the fix needs to be in your code, as David has already mentioned. And in addition: I would *strongly* advise to leave the Storable () instance as-is, I'm quite sure that otherwise tons of code will break in mysterious ways, undetected by any compiler. Cheers, S. -------------- next part -------------- An HTML attachment was scrubbed... URL: From matthewtpickering at gmail.com Wed Jan 5 10:26:25 2022 From: matthewtpickering at gmail.com (Matthew Pickering) Date: Wed, 5 Jan 2022 10:26:25 +0000 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: I agree with the other replies to this thread, I just reply to point out the Binary instance for () is the same. On Wed, Jan 5, 2022 at 8:01 AM Harendra Kumar wrote: > > The Storable instance of () is defined in the "Foreign.Storable" > module of the "base" package as follows: > > instance Storable () where > sizeOf _ = 0 > alignment _ = 1 > peek _ = return () > poke _ _ = return () > > The size of () is defined as 0. It sounds absurd for a Storable to > have a size of 0? This means that we can read an infinite number of () > type values out of nothing (no memory location required) or store an > infinite number of () type values without even requiring a memory > location to write to. > > This is causing a practical problem in our Storable array > implementation. The array is constrained to a Storable type. Since () > has a Storable instance, one can store () in the Storable array. But > it causes a problem because we determine the array element size using > sizeOf on the type. For () type it turns out to be 0. Essentially, the > array of () would always be of size 0. Now, we cannot determine the > length of the array from its byte length as you could store infinite > such elements in an empty array. The Storable instance of () seems to > be an oddity and makes us use a special case everywhere in the code to > handle this, and this special casing makes it highly prone to errors > when we change code. > > Can this be fixed? Is there a compelling argument to keep it like > this? A possible fix could be to represent it by a single byte in > memory which can be discarded when reading or writing. Another > alternative is to not provide a Storable instance for it at all. Let > the users write their own if they need it. > > If you think this does not have a problem, can you suggest how to > elegantly handle the array implementation problem as I described > above? > > Thanks, > Harendra > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries From godzbanebane at gmail.com Wed Jan 5 10:39:29 2022 From: godzbanebane at gmail.com (Georgi Lyubenov) Date: Wed, 5 Jan 2022 12:39:29 +0200 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: I have an additional question: It is true that in a strict/unboxed language, the type of () is sufficient to reproduce its value. However, here, trying to store undefined :: () is no different from trying to store () :: (). Is this difference in behaviour with other instances of Storable (where presumably trying to store undefined will blow up, as there is indeed some work to do there) intentionally ignored? On Wed, Jan 5, 2022 at 12:26 PM Matthew Pickering < matthewtpickering at gmail.com> wrote: > I agree with the other replies to this thread, I just reply to point > out the Binary instance for () is the same. > > On Wed, Jan 5, 2022 at 8:01 AM Harendra Kumar > wrote: > > > > The Storable instance of () is defined in the "Foreign.Storable" > > module of the "base" package as follows: > > > > instance Storable () where > > sizeOf _ = 0 > > alignment _ = 1 > > peek _ = return () > > poke _ _ = return () > > > > The size of () is defined as 0. It sounds absurd for a Storable to > > have a size of 0? This means that we can read an infinite number of () > > type values out of nothing (no memory location required) or store an > > infinite number of () type values without even requiring a memory > > location to write to. > > > > This is causing a practical problem in our Storable array > > implementation. The array is constrained to a Storable type. Since () > > has a Storable instance, one can store () in the Storable array. But > > it causes a problem because we determine the array element size using > > sizeOf on the type. For () type it turns out to be 0. Essentially, the > > array of () would always be of size 0. Now, we cannot determine the > > length of the array from its byte length as you could store infinite > > such elements in an empty array. The Storable instance of () seems to > > be an oddity and makes us use a special case everywhere in the code to > > handle this, and this special casing makes it highly prone to errors > > when we change code. > > > > Can this be fixed? Is there a compelling argument to keep it like > > this? A possible fix could be to represent it by a single byte in > > memory which can be discarded when reading or writing. Another > > alternative is to not provide a Storable instance for it at all. Let > > the users write their own if they need it. > > > > If you think this does not have a problem, can you suggest how to > > elegantly handle the array implementation problem as I described > > above? > > > > Thanks, > > Harendra > > _______________________________________________ > > Libraries mailing list > > Libraries at haskell.org > > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.feuer at gmail.com Wed Jan 5 10:50:46 2022 From: david.feuer at gmail.com (David Feuer) Date: Wed, 5 Jan 2022 05:50:46 -0500 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: It is a bit peculiar that that would be so. Maybe there's some efficiency reason, but it doesn't seem very strongly motivated. On Wed, Jan 5, 2022 at 5:40 AM Georgi Lyubenov wrote: > > I have an additional question: > > It is true that in a strict/unboxed language, the type of () is sufficient to reproduce its value. However, here, trying to store undefined :: () is no different from trying to store () :: (). Is this difference in behaviour with other instances of Storable (where presumably trying to store undefined will blow up, as there is indeed some work to do there) intentionally ignored? > > On Wed, Jan 5, 2022 at 12:26 PM Matthew Pickering wrote: >> >> I agree with the other replies to this thread, I just reply to point >> out the Binary instance for () is the same. >> >> On Wed, Jan 5, 2022 at 8:01 AM Harendra Kumar wrote: >> > >> > The Storable instance of () is defined in the "Foreign.Storable" >> > module of the "base" package as follows: >> > >> > instance Storable () where >> > sizeOf _ = 0 >> > alignment _ = 1 >> > peek _ = return () >> > poke _ _ = return () >> > >> > The size of () is defined as 0. It sounds absurd for a Storable to >> > have a size of 0? This means that we can read an infinite number of () >> > type values out of nothing (no memory location required) or store an >> > infinite number of () type values without even requiring a memory >> > location to write to. >> > >> > This is causing a practical problem in our Storable array >> > implementation. The array is constrained to a Storable type. Since () >> > has a Storable instance, one can store () in the Storable array. But >> > it causes a problem because we determine the array element size using >> > sizeOf on the type. For () type it turns out to be 0. Essentially, the >> > array of () would always be of size 0. Now, we cannot determine the >> > length of the array from its byte length as you could store infinite >> > such elements in an empty array. The Storable instance of () seems to >> > be an oddity and makes us use a special case everywhere in the code to >> > handle this, and this special casing makes it highly prone to errors >> > when we change code. >> > >> > Can this be fixed? Is there a compelling argument to keep it like >> > this? A possible fix could be to represent it by a single byte in >> > memory which can be discarded when reading or writing. Another >> > alternative is to not provide a Storable instance for it at all. Let >> > the users write their own if they need it. >> > >> > If you think this does not have a problem, can you suggest how to >> > elegantly handle the array implementation problem as I described >> > above? >> > >> > Thanks, >> > Harendra >> > _______________________________________________ >> > Libraries mailing list >> > Libraries at haskell.org >> > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries From svenpanne at gmail.com Wed Jan 5 10:51:14 2022 From: svenpanne at gmail.com (Sven Panne) Date: Wed, 5 Jan 2022 11:51:14 +0100 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: Am Mi., 5. Jan. 2022 um 11:40 Uhr schrieb Georgi Lyubenov < godzbanebane at gmail.com>: > [...] However, here, trying to store undefined :: () is no different from > trying to store () :: (). Is this difference in behaviour with other > instances of Storable (where presumably trying to store undefined will blow > up, as there is indeed some work to do there) intentionally ignored? > Good point, this might be seen as a bug/inconsistency of Storable (): peek and poke are not strict in their arguments, while probably all(?) other instances are. But https://www.haskell.org/onlinereport/haskell2010/haskellch37.html doesn't require this, so I would be reluctant to really call this a bug. Changing this can have "interesting" effects on the ecosystem, too, who knows? Again, this would be a change where the compiler doesn't help you. Cheers, S. -------------- next part -------------- An HTML attachment was scrubbed... URL: From roma at ro-che.info Wed Jan 5 11:05:02 2022 From: roma at ro-che.info (Roman Cheplyaka) Date: Wed, 5 Jan 2022 13:05:02 +0200 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: <39864779-17b2-8005-db31-466396b3e361@ro-che.info> On 05/01/2022 12.39, Georgi Lyubenov wrote: > I have an additional question: > > It is true that in a strict/unboxed language, the type of () is > sufficient to reproduce its value. However, here, trying to store > undefined :: () is no different from trying to store () :: (). Is this > difference in behaviour with other instances of Storable (where > presumably trying to store undefined will blow up, as there is indeed > some work to do there) intentionally ignored? If you look at it from the strict-by-default point of view, it does appear inconsistent with the other instances. However, if you look at it from the non-strict-by-default point of view, which is arguably more native to Haskell, then all instances follow the same principle: they are maximally non-strict. It's just when you store anything non-trivial, you are forced to be strict in order to fulfill the task, but you never add any gratuitous strictness. Roman From harendra.kumar at gmail.com Wed Jan 5 11:14:32 2022 From: harendra.kumar at gmail.com (Harendra Kumar) Date: Wed, 5 Jan 2022 16:44:32 +0530 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: On Wed, 5 Jan 2022 at 13:44, David Feuer wrote: > > I don't think it's broken; I think your length calculation is broken. Instead of asking every use of () to take an extra byte, why don't you just store a word saying how long your array is? We can always say that the application is broken and not the underlying system as long the problem can be circumvented in the application. Of course the problem can be circumvented in our code and that is what we are doing, but these semantics for the () instance makes it harder and not very elegant. It is debatable what is broken. That's the reason I wanted to raise this for discussion. We already store the length in the array but the length is in the form of number of bytes and not the number of elements, we store the start and end pointers in memory and compute the byte length from that. And the () Storable instance does not allow us to convert bytes back to the number of elements. And that is actually the crux of the problem. There are reasons for storing pointers which may be irrelevant for this discussion. But the point is that this instance forces us to use a different representation where we have to store the number of elements in the array. Which I think is unnecessary for Storable arrays as long as the Storable type has a concrete representation with a definite size. I would like to think that if something is Storable then it should have a concrete memory representation. peek and poke operations themselves indicate this fundamental nature of Storable semantics. peek means we are reading from a memory location and poke means that we are writing to a memory location. In the case of () we are just pretending to read or write on peek/poke, we are not storing or retrieving something. This in my opinion should not be how Storable should behave. I think this should not be about optimising for that extra byte. That optimization is bogus. We are saying look, we can store something in memory without even consuming any space. What's the point of doing that? In this case we are doing that just because we can. To take this argument further we can also say that we can compress some byte sequences and it will take up less space. But that's harder and it will completely screw up the simple semantics so we won't do that. Let's screw it only a little bit, because it's easier to do that. If we do not want to consume space for () then better not have a Storable instance for it, leave it to the users. And if we really want to store it then let it behave in the same way as any other mere mortal storable would, so what if it takes up some space. Why not have simple, uniform semantics for all cases? I think saving that extra byte for this particular case is an overkill and leading to a bigger cost in applications without actually saving anything worthwhile. -harendra > > Alternatively, you could probably avoid special cases with a newtype: > > newtype NZS a = NZS { unNZS :: a } > > instance Storable a => Storable (NZS a) where > sizeof > | sizeof (undefined @a) == 0 > = const 1 > | otherwise = sizeof .# unNZS > alignment = alignment .# unNZS > peekElemOff = coerce (peekElemOff @a) > .... > > On Wed, Jan 5, 2022, 3:01 AM Harendra Kumar wrote: >> >> The Storable instance of () is defined in the "Foreign.Storable" >> module of the "base" package as follows: >> >> instance Storable () where >> sizeOf _ = 0 >> alignment _ = 1 >> peek _ = return () >> poke _ _ = return () >> >> The size of () is defined as 0. It sounds absurd for a Storable to >> have a size of 0? This means that we can read an infinite number of () >> type values out of nothing (no memory location required) or store an >> infinite number of () type values without even requiring a memory >> location to write to. >> >> This is causing a practical problem in our Storable array >> implementation. The array is constrained to a Storable type. Since () >> has a Storable instance, one can store () in the Storable array. But >> it causes a problem because we determine the array element size using >> sizeOf on the type. For () type it turns out to be 0. Essentially, the >> array of () would always be of size 0. Now, we cannot determine the >> length of the array from its byte length as you could store infinite >> such elements in an empty array. The Storable instance of () seems to >> be an oddity and makes us use a special case everywhere in the code to >> handle this, and this special casing makes it highly prone to errors >> when we change code. >> >> Can this be fixed? Is there a compelling argument to keep it like >> this? A possible fix could be to represent it by a single byte in >> memory which can be discarded when reading or writing. Another >> alternative is to not provide a Storable instance for it at all. Let >> the users write their own if they need it. >> >> If you think this does not have a problem, can you suggest how to >> elegantly handle the array implementation problem as I described >> above? >> >> Thanks, >> Harendra >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries From david.feuer at gmail.com Wed Jan 5 11:41:24 2022 From: david.feuer at gmail.com (David Feuer) Date: Wed, 5 Jan 2022 06:41:24 -0500 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: No. Consider a type like this: data Foo a = Foo !Int !a instance Storable a => Storable (Foo a) where ... Now if a happens to be (), we pay only one word per Foo. You want us to pay more so you can do your calculation more efficiently. That doesn't seem quite fair. You have another option: don't use (). Just write your own version with a different Storable instance and use that. Or use a newtype wrapper like I suggested. On Wed, Jan 5, 2022, 6:14 AM Harendra Kumar wrote: > On Wed, 5 Jan 2022 at 13:44, David Feuer wrote: > > > > I don't think it's broken; I think your length calculation is broken. > Instead of asking every use of () to take an extra byte, why don't you just > store a word saying how long your array is? > > We can always say that the application is broken and not the > underlying system as long the problem can be circumvented in the > application. Of course the problem can be circumvented in our code and > that is what we are doing, but these semantics for the () instance > makes it harder and not very elegant. It is debatable what is broken. > That's the reason I wanted to raise this for discussion. > > We already store the length in the array but the length is in the form > of number of bytes and not the number of elements, we store the start > and end pointers in memory and compute the byte length from that. And > the () Storable instance does not allow us to convert bytes back to > the number of elements. And that is actually the crux of the problem. > > There are reasons for storing pointers which may be irrelevant for > this discussion. But the point is that this instance forces us to use > a different representation where we have to store the number of > elements in the array. Which I think is unnecessary for Storable > arrays as long as the Storable type has a concrete representation with > a definite size. > > I would like to think that if something is Storable then it should > have a concrete memory representation. peek and poke operations > themselves indicate this fundamental nature of Storable semantics. > peek means we are reading from a memory location and poke means that > we are writing to a memory location. In the case of () we are just > pretending to read or write on peek/poke, we are not storing or > retrieving something. This in my opinion should not be how Storable > should behave. > > I think this should not be about optimising for that extra byte. That > optimization is bogus. We are saying look, we can store something in > memory without even consuming any space. What's the point of doing > that? In this case we are doing that just because we can. To take > this argument further we can also say that we can compress some byte > sequences and it will take up less space. But that's harder and it > will completely screw up the simple semantics so we won't do that. > Let's screw it only a little bit, because it's easier to do that. > > If we do not want to consume space for () then better not have a > Storable instance for it, leave it to the users. And if we really want > to store it then let it behave in the same way as any other mere > mortal storable would, so what if it takes up some space. Why not have > simple, uniform semantics for all cases? I think saving that extra > byte for this particular case is an overkill and leading to a bigger > cost in applications without actually saving anything worthwhile. > > -harendra > > > > > Alternatively, you could probably avoid special cases with a newtype: > > > > newtype NZS a = NZS { unNZS :: a } > > > > instance Storable a => Storable (NZS a) where > > sizeof > > | sizeof (undefined @a) == 0 > > = const 1 > > | otherwise = sizeof .# unNZS > > alignment = alignment .# unNZS > > peekElemOff = coerce (peekElemOff @a) > > .... > > > > On Wed, Jan 5, 2022, 3:01 AM Harendra Kumar > wrote: > >> > >> The Storable instance of () is defined in the "Foreign.Storable" > >> module of the "base" package as follows: > >> > >> instance Storable () where > >> sizeOf _ = 0 > >> alignment _ = 1 > >> peek _ = return () > >> poke _ _ = return () > >> > >> The size of () is defined as 0. It sounds absurd for a Storable to > >> have a size of 0? This means that we can read an infinite number of () > >> type values out of nothing (no memory location required) or store an > >> infinite number of () type values without even requiring a memory > >> location to write to. > >> > >> This is causing a practical problem in our Storable array > >> implementation. The array is constrained to a Storable type. Since () > >> has a Storable instance, one can store () in the Storable array. But > >> it causes a problem because we determine the array element size using > >> sizeOf on the type. For () type it turns out to be 0. Essentially, the > >> array of () would always be of size 0. Now, we cannot determine the > >> length of the array from its byte length as you could store infinite > >> such elements in an empty array. The Storable instance of () seems to > >> be an oddity and makes us use a special case everywhere in the code to > >> handle this, and this special casing makes it highly prone to errors > >> when we change code. > >> > >> Can this be fixed? Is there a compelling argument to keep it like > >> this? A possible fix could be to represent it by a single byte in > >> memory which can be discarded when reading or writing. Another > >> alternative is to not provide a Storable instance for it at all. Let > >> the users write their own if they need it. > >> > >> If you think this does not have a problem, can you suggest how to > >> elegantly handle the array implementation problem as I described > >> above? > >> > >> Thanks, > >> Harendra > >> _______________________________________________ > >> Libraries mailing list > >> Libraries at haskell.org > >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From lemming at henning-thielemann.de Wed Jan 5 11:41:41 2022 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Wed, 5 Jan 2022 12:41:41 +0100 (CET) Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: On Wed, 5 Jan 2022, Harendra Kumar wrote: > If we do not want to consume space for () then better not have a > Storable instance for it, leave it to the users. And if we really want > to store it then let it behave in the same way as any other mere > mortal storable would, so what if it takes up some space. Why not have > simple, uniform semantics for all cases? I think saving that extra > byte for this particular case is an overkill and leading to a bigger > cost in applications without actually saving anything worthwhile. Maybe the Storable class is not the right one for your application. I came to this conclusion for my llvm-tf package for exchanging data between LLVM code and Haskell. Today I distinguish between base:Storable and my own LLVM.Storable class. They share implementations for Word8, Int16 and so on, but e.g. for Bool they differ (LLVM: one byte, base: four bytes) and LLVM.Storable supports tuples, whereas base:Storable does not. One of my LLVM applications works this way: I have an Arrow with existentially quantified state. If an arrow does not need a state, the state type is (). If I combine two arrows with (***) then I need to bundle their states in a pair. Thus I need to store () and I need to store nested tuples where some members are (). () is mapped to LLVM's empty tuple {}, and {} consumes no space. By using (***), the state type grows, but the required memory may stay the same. That is, () consuming no space can be very useful. However, that argument cannot be directly transfered to base:Storable, because base:Storable is not intended for tuples. If you find that your application needs tuples, then you better define your own Storable class, anyway. From harendra.kumar at gmail.com Wed Jan 5 12:09:49 2022 From: harendra.kumar at gmail.com (Harendra Kumar) Date: Wed, 5 Jan 2022 17:39:49 +0530 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: It is hard to objectively or mathematically prove which option is better. As a library designer I do not like to think only from the perspective of my current problem at hand but in general what is the right thing. In my opinion, the right thing here is to have uniform semantics with a non-zero size for objects that are stored or retrieved from memory. If I were the owner of the base package I would do that. This optimization in my opinion is a micro-optimization which is irrelevant in the larger scheme of things. If someone wants to optimise for this case there could be ways to do that. But again it is subjective - this vs that. It is a different discussion whether it is a good idea to change the instance because it might break things. This instance came into being in GHC 8.0. There must be a reason for that, maybe someone on this list knows. I wonder how many users there are. -harendra On Wed, 5 Jan 2022 at 17:11, David Feuer wrote: > > No. Consider a type like this: > > data Foo a = Foo !Int !a > > instance Storable a => Storable (Foo a) where ... > > Now if a happens to be (), we pay only one word per Foo. You want us to pay more so you can do your calculation more efficiently. That doesn't seem quite fair. You have another option: don't use (). Just write your own version with a different Storable instance and use that. Or use a newtype wrapper like I suggested. > > On Wed, Jan 5, 2022, 6:14 AM Harendra Kumar wrote: >> >> On Wed, 5 Jan 2022 at 13:44, David Feuer wrote: >> > >> > I don't think it's broken; I think your length calculation is broken. Instead of asking every use of () to take an extra byte, why don't you just store a word saying how long your array is? >> >> We can always say that the application is broken and not the >> underlying system as long the problem can be circumvented in the >> application. Of course the problem can be circumvented in our code and >> that is what we are doing, but these semantics for the () instance >> makes it harder and not very elegant. It is debatable what is broken. >> That's the reason I wanted to raise this for discussion. >> >> We already store the length in the array but the length is in the form >> of number of bytes and not the number of elements, we store the start >> and end pointers in memory and compute the byte length from that. And >> the () Storable instance does not allow us to convert bytes back to >> the number of elements. And that is actually the crux of the problem. >> >> There are reasons for storing pointers which may be irrelevant for >> this discussion. But the point is that this instance forces us to use >> a different representation where we have to store the number of >> elements in the array. Which I think is unnecessary for Storable >> arrays as long as the Storable type has a concrete representation with >> a definite size. >> >> I would like to think that if something is Storable then it should >> have a concrete memory representation. peek and poke operations >> themselves indicate this fundamental nature of Storable semantics. >> peek means we are reading from a memory location and poke means that >> we are writing to a memory location. In the case of () we are just >> pretending to read or write on peek/poke, we are not storing or >> retrieving something. This in my opinion should not be how Storable >> should behave. >> >> I think this should not be about optimising for that extra byte. That >> optimization is bogus. We are saying look, we can store something in >> memory without even consuming any space. What's the point of doing >> that? In this case we are doing that just because we can. To take >> this argument further we can also say that we can compress some byte >> sequences and it will take up less space. But that's harder and it >> will completely screw up the simple semantics so we won't do that. >> Let's screw it only a little bit, because it's easier to do that. >> >> If we do not want to consume space for () then better not have a >> Storable instance for it, leave it to the users. And if we really want >> to store it then let it behave in the same way as any other mere >> mortal storable would, so what if it takes up some space. Why not have >> simple, uniform semantics for all cases? I think saving that extra >> byte for this particular case is an overkill and leading to a bigger >> cost in applications without actually saving anything worthwhile. >> >> -harendra >> >> > >> > Alternatively, you could probably avoid special cases with a newtype: >> > >> > newtype NZS a = NZS { unNZS :: a } >> > >> > instance Storable a => Storable (NZS a) where >> > sizeof >> > | sizeof (undefined @a) == 0 >> > = const 1 >> > | otherwise = sizeof .# unNZS >> > alignment = alignment .# unNZS >> > peekElemOff = coerce (peekElemOff @a) >> > .... >> > >> > On Wed, Jan 5, 2022, 3:01 AM Harendra Kumar wrote: >> >> >> >> The Storable instance of () is defined in the "Foreign.Storable" >> >> module of the "base" package as follows: >> >> >> >> instance Storable () where >> >> sizeOf _ = 0 >> >> alignment _ = 1 >> >> peek _ = return () >> >> poke _ _ = return () >> >> >> >> The size of () is defined as 0. It sounds absurd for a Storable to >> >> have a size of 0? This means that we can read an infinite number of () >> >> type values out of nothing (no memory location required) or store an >> >> infinite number of () type values without even requiring a memory >> >> location to write to. >> >> >> >> This is causing a practical problem in our Storable array >> >> implementation. The array is constrained to a Storable type. Since () >> >> has a Storable instance, one can store () in the Storable array. But >> >> it causes a problem because we determine the array element size using >> >> sizeOf on the type. For () type it turns out to be 0. Essentially, the >> >> array of () would always be of size 0. Now, we cannot determine the >> >> length of the array from its byte length as you could store infinite >> >> such elements in an empty array. The Storable instance of () seems to >> >> be an oddity and makes us use a special case everywhere in the code to >> >> handle this, and this special casing makes it highly prone to errors >> >> when we change code. >> >> >> >> Can this be fixed? Is there a compelling argument to keep it like >> >> this? A possible fix could be to represent it by a single byte in >> >> memory which can be discarded when reading or writing. Another >> >> alternative is to not provide a Storable instance for it at all. Let >> >> the users write their own if they need it. >> >> >> >> If you think this does not have a problem, can you suggest how to >> >> elegantly handle the array implementation problem as I described >> >> above? >> >> >> >> Thanks, >> >> Harendra >> >> _______________________________________________ >> >> Libraries mailing list >> >> Libraries at haskell.org >> >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries From svenpanne at gmail.com Wed Jan 5 12:12:32 2022 From: svenpanne at gmail.com (Sven Panne) Date: Wed, 5 Jan 2022 13:12:32 +0100 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: Am Mi., 5. Jan. 2022 um 12:42 Uhr schrieb David Feuer : > No. Consider a type like this: > > data Foo a = Foo !Int !a > > instance Storable a => Storable (Foo a) where ... > > Now if a happens to be (), we pay only one word per Foo. [...] > This is exactly the kind of breakage I had in mind: With the proposed change, the storage layout would change, and the compiler wouldn't warn you about that at all. Note that I'm not arguing about memory efficiency, it's all about a subtle semantic change for the sake of a single library, wanting to change something which was in place for 10-20 years. Seems like an extremely bad move from the POV of the Haskell ecosystem: It's exactly this kind of ad hoc changes which annoys people. -------------- next part -------------- An HTML attachment was scrubbed... URL: From allbery.b at gmail.com Wed Jan 5 12:15:07 2022 From: allbery.b at gmail.com (Brandon Allbery) Date: Wed, 5 Jan 2022 07:15:07 -0500 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: "Mathematically" doesn't seem the point of Storable; it's about exchanging values with foreign functions, which in practice means C. What C type does () correspond to? (It's not void, since that has no values.) On Wed, Jan 5, 2022 at 7:10 AM Harendra Kumar wrote: > > It is hard to objectively or mathematically prove which option is > better. As a library designer I do not like to think only from the > perspective of my current problem at hand but in general what is the > right thing. In my opinion, the right thing here is to have uniform > semantics with a non-zero size for objects that are stored or > retrieved from memory. If I were the owner of the base package I would > do that. This optimization in my opinion is a micro-optimization which > is irrelevant in the larger scheme of things. If someone wants to > optimise for this case there could be ways to do that. But again it is > subjective - this vs that. > > It is a different discussion whether it is a good idea to change the > instance because it might break things. This instance came into being > in GHC 8.0. There must be a reason for that, maybe someone on this > list knows. I wonder how many users there are. > > -harendra > > On Wed, 5 Jan 2022 at 17:11, David Feuer wrote: > > > > No. Consider a type like this: > > > > data Foo a = Foo !Int !a > > > > instance Storable a => Storable (Foo a) where ... > > > > Now if a happens to be (), we pay only one word per Foo. You want us to pay more so you can do your calculation more efficiently. That doesn't seem quite fair. You have another option: don't use (). Just write your own version with a different Storable instance and use that. Or use a newtype wrapper like I suggested. > > > > On Wed, Jan 5, 2022, 6:14 AM Harendra Kumar wrote: > >> > >> On Wed, 5 Jan 2022 at 13:44, David Feuer wrote: > >> > > >> > I don't think it's broken; I think your length calculation is broken. Instead of asking every use of () to take an extra byte, why don't you just store a word saying how long your array is? > >> > >> We can always say that the application is broken and not the > >> underlying system as long the problem can be circumvented in the > >> application. Of course the problem can be circumvented in our code and > >> that is what we are doing, but these semantics for the () instance > >> makes it harder and not very elegant. It is debatable what is broken. > >> That's the reason I wanted to raise this for discussion. > >> > >> We already store the length in the array but the length is in the form > >> of number of bytes and not the number of elements, we store the start > >> and end pointers in memory and compute the byte length from that. And > >> the () Storable instance does not allow us to convert bytes back to > >> the number of elements. And that is actually the crux of the problem. > >> > >> There are reasons for storing pointers which may be irrelevant for > >> this discussion. But the point is that this instance forces us to use > >> a different representation where we have to store the number of > >> elements in the array. Which I think is unnecessary for Storable > >> arrays as long as the Storable type has a concrete representation with > >> a definite size. > >> > >> I would like to think that if something is Storable then it should > >> have a concrete memory representation. peek and poke operations > >> themselves indicate this fundamental nature of Storable semantics. > >> peek means we are reading from a memory location and poke means that > >> we are writing to a memory location. In the case of () we are just > >> pretending to read or write on peek/poke, we are not storing or > >> retrieving something. This in my opinion should not be how Storable > >> should behave. > >> > >> I think this should not be about optimising for that extra byte. That > >> optimization is bogus. We are saying look, we can store something in > >> memory without even consuming any space. What's the point of doing > >> that? In this case we are doing that just because we can. To take > >> this argument further we can also say that we can compress some byte > >> sequences and it will take up less space. But that's harder and it > >> will completely screw up the simple semantics so we won't do that. > >> Let's screw it only a little bit, because it's easier to do that. > >> > >> If we do not want to consume space for () then better not have a > >> Storable instance for it, leave it to the users. And if we really want > >> to store it then let it behave in the same way as any other mere > >> mortal storable would, so what if it takes up some space. Why not have > >> simple, uniform semantics for all cases? I think saving that extra > >> byte for this particular case is an overkill and leading to a bigger > >> cost in applications without actually saving anything worthwhile. > >> > >> -harendra > >> > >> > > >> > Alternatively, you could probably avoid special cases with a newtype: > >> > > >> > newtype NZS a = NZS { unNZS :: a } > >> > > >> > instance Storable a => Storable (NZS a) where > >> > sizeof > >> > | sizeof (undefined @a) == 0 > >> > = const 1 > >> > | otherwise = sizeof .# unNZS > >> > alignment = alignment .# unNZS > >> > peekElemOff = coerce (peekElemOff @a) > >> > .... > >> > > >> > On Wed, Jan 5, 2022, 3:01 AM Harendra Kumar wrote: > >> >> > >> >> The Storable instance of () is defined in the "Foreign.Storable" > >> >> module of the "base" package as follows: > >> >> > >> >> instance Storable () where > >> >> sizeOf _ = 0 > >> >> alignment _ = 1 > >> >> peek _ = return () > >> >> poke _ _ = return () > >> >> > >> >> The size of () is defined as 0. It sounds absurd for a Storable to > >> >> have a size of 0? This means that we can read an infinite number of () > >> >> type values out of nothing (no memory location required) or store an > >> >> infinite number of () type values without even requiring a memory > >> >> location to write to. > >> >> > >> >> This is causing a practical problem in our Storable array > >> >> implementation. The array is constrained to a Storable type. Since () > >> >> has a Storable instance, one can store () in the Storable array. But > >> >> it causes a problem because we determine the array element size using > >> >> sizeOf on the type. For () type it turns out to be 0. Essentially, the > >> >> array of () would always be of size 0. Now, we cannot determine the > >> >> length of the array from its byte length as you could store infinite > >> >> such elements in an empty array. The Storable instance of () seems to > >> >> be an oddity and makes us use a special case everywhere in the code to > >> >> handle this, and this special casing makes it highly prone to errors > >> >> when we change code. > >> >> > >> >> Can this be fixed? Is there a compelling argument to keep it like > >> >> this? A possible fix could be to represent it by a single byte in > >> >> memory which can be discarded when reading or writing. Another > >> >> alternative is to not provide a Storable instance for it at all. Let > >> >> the users write their own if they need it. > >> >> > >> >> If you think this does not have a problem, can you suggest how to > >> >> elegantly handle the array implementation problem as I described > >> >> above? > >> >> > >> >> Thanks, > >> >> Harendra > >> >> _______________________________________________ > >> >> Libraries mailing list > >> >> Libraries at haskell.org > >> >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries -- brandon s allbery kf8nh allbery.b at gmail.com From svenpanne at gmail.com Wed Jan 5 12:18:20 2022 From: svenpanne at gmail.com (Sven Panne) Date: Wed, 5 Jan 2022 13:18:20 +0100 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: Am Mi., 5. Jan. 2022 um 13:11 Uhr schrieb Harendra Kumar < harendra.kumar at gmail.com>: > [...] In my opinion, the right thing here is to have uniform > semantics with a non-zero size for objects that are stored or > retrieved from memory. Which way is more uniform seems to depend on your POV, and base's choice has been made a long time ago, so that ship has sailed... > If I were the owner of the base package I would do that. Then I'm quite happy that you aren't. ;-) API breakages should have a *very* good reason, not just that it makes life easier for a single library. > This optimization in my opinion is a micro-optimization which > is irrelevant in the larger scheme of things. If someone wants to > optimise for this case there could be ways to do that. But again it is > subjective - this vs that. > IIRC there were no deep thoughts or arguments about optimizations at all at that time, but I still find the Storable () instance OK in its current state today. -------------- next part -------------- An HTML attachment was scrubbed... URL: From harendra.kumar at gmail.com Wed Jan 5 12:29:02 2022 From: harendra.kumar at gmail.com (Harendra Kumar) Date: Wed, 5 Jan 2022 17:59:02 +0530 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: On Wed, 5 Jan 2022 at 17:48, Sven Panne wrote: > Then I'm quite happy that you aren't. ;-) API breakages should have a *very* good reason, not just that it makes life easier for a single library. Sure. But I did not mean that I am hell bent to change it now, I think I said that changing it now is a different discussion. -harendra From tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk Wed Jan 5 12:38:44 2022 From: tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk (Tom Ellis) Date: Wed, 5 Jan 2022 12:38:44 +0000 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: On Wed, Jan 05, 2022 at 05:39:49PM +0530, Harendra Kumar wrote: > It is hard to objectively or mathematically prove which option is > better. Well, let's give it a go. One condition that instances might be required to satisfy is do { poke p a; peek p } == do { poke p a; evaluate a } and evaluating these expressions should touch at most `sizeof a` bytes starting from `p`. If further you impose the reasonable condition that `sizeof a` should be as small as possible then this fixes the behaviour of the () instance. (Other reasonable conditions are available.) The existing `Storable ()` instance does *not* satisfy this condition, because it is not sufficiently strict. That's orthogonal to Harendra's complaint though. It does satisfy the "as small as possible" part. On the other hand, despite being no expert of Storable, it seems to me the class exists merely to conveniently read to and write from raw memory. The () instance is useless for this purpose so I'm not sure why it exists. If the purpose of the class were to be a general purpose serialisation API then the () instance *would* make sense, but then we'd also have an (a, b) instance too, which we don't. If it were up to me I probably would not have allowed the `Storable ()` instance, and instead I would have designed a "serialise to a raw buffer" API *on top of* Storable (if that's what people really wanted). Tom (See https://www.stackage.org/haddock/lts-18.21/base-4.14.3.0/Foreign-Storable.html#t:Storable) From oleg.grenrus at iki.fi Wed Jan 5 13:42:44 2022 From: oleg.grenrus at iki.fi (Oleg Grenrus) Date: Wed, 5 Jan 2022 15:42:44 +0200 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: https://gcc.gnu.org/onlinedocs/gcc-4.4.3/gcc/Empty-Structures.html#Empty-Structures > GCC permits a C structure to have no members:      struct empty {      }; > The structure will have size zero. In C++, empty structures are part of the language. G++ treats empty structures as if they had a single member of type char. so it's either GCC is absurd, or it isn't. Standard C also supports zero-width members (to force alignment), whether to use () for that or not in modelling Haskell-Storable structs. Could be useful.https://stackoverflow.com/questions/13802728/what-is-zero-width-bit-field - Oleg On 5.1.2022 10.01, Harendra Kumar wrote: > The Storable instance of () is defined in the "Foreign.Storable" > module of the "base" package as follows: > > instance Storable () where > sizeOf _ = 0 > alignment _ = 1 > peek _ = return () > poke _ _ = return () > > The size of () is defined as 0. It sounds absurd for a Storable to > have a size of 0? This means that we can read an infinite number of () > type values out of nothing (no memory location required) or store an > infinite number of () type values without even requiring a memory > location to write to. > > This is causing a practical problem in our Storable array > implementation. The array is constrained to a Storable type. Since () > has a Storable instance, one can store () in the Storable array. But > it causes a problem because we determine the array element size using > sizeOf on the type. For () type it turns out to be 0. Essentially, the > array of () would always be of size 0. Now, we cannot determine the > length of the array from its byte length as you could store infinite > such elements in an empty array. The Storable instance of () seems to > be an oddity and makes us use a special case everywhere in the code to > handle this, and this special casing makes it highly prone to errors > when we change code. > > Can this be fixed? Is there a compelling argument to keep it like > this? A possible fix could be to represent it by a single byte in > memory which can be discarded when reading or writing. Another > alternative is to not provide a Storable instance for it at all. Let > the users write their own if they need it. > > If you think this does not have a problem, can you suggest how to > elegantly handle the array implementation problem as I described > above? > > Thanks, > Harendra > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries From harendra.kumar at gmail.com Wed Jan 5 13:49:34 2022 From: harendra.kumar at gmail.com (Harendra Kumar) Date: Wed, 5 Jan 2022 19:19:34 +0530 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: On Wed, 5 Jan 2022 at 18:09, Tom Ellis wrote: > > On the other hand, despite being no expert of Storable, it seems to me > the class exists merely to conveniently read to and write from raw > memory. The () instance is useless for this purpose so I'm not sure > why it exists. If the purpose of the class were to be a general > purpose serialisation API then the () instance *would* make sense, but > then we'd also have an (a, b) instance too, which we don't. Exactly! From fumiexcel at gmail.com Wed Jan 5 13:51:48 2022 From: fumiexcel at gmail.com (Fumiaki Kinoshita) Date: Wed, 5 Jan 2022 22:51:48 +0900 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: I'm the author of the instance [1]. One of my libraries uses Storable vectors to represent buffers of audio samples[1]. When the user doesn't need input, they leave the type of input vector to be `Vector ()`, which is totally valid and reasonable under the constraint that the input buffer and the output buffer have the same size. > This means that we can read an infinite number of () type values out of nothing (no memory location required) or store an infinite number of () type values without even requiring a memory location to write to. Yes, we can read an infinite number of () without reading anything in memory. That's intended, and it is less arbitrary than defining () as a single byte object. Perhaps you may want to reconsider the design of your array implementation before roasting this instance as "broken" and "absurd". [1] https://gitlab.haskell.org/ghc/ghc/-/commit/97843d0b10cac3912a85329ebcb8ed1a68f71b34 [2] https://github.com/fumieval/bindings-portaudio/blob/4e49c50d19d141062e7a75a5e39d8c8388456309/System/PortAudio.hs#L252 2022年1月5日(水) 17:01 Harendra Kumar : > The Storable instance of () is defined in the "Foreign.Storable" > module of the "base" package as follows: > > instance Storable () where > sizeOf _ = 0 > alignment _ = 1 > peek _ = return () > poke _ _ = return () > > The size of () is defined as 0. It sounds absurd for a Storable to > have a size of 0? This means that we can read an infinite number of () > type values out of nothing (no memory location required) or store an > infinite number of () type values without even requiring a memory > location to write to. > > This is causing a practical problem in our Storable array > implementation. The array is constrained to a Storable type. Since () > has a Storable instance, one can store () in the Storable array. But > it causes a problem because we determine the array element size using > sizeOf on the type. For () type it turns out to be 0. Essentially, the > array of () would always be of size 0. Now, we cannot determine the > length of the array from its byte length as you could store infinite > such elements in an empty array. The Storable instance of () seems to > be an oddity and makes us use a special case everywhere in the code to > handle this, and this special casing makes it highly prone to errors > when we change code. > > Can this be fixed? Is there a compelling argument to keep it like > this? A possible fix could be to represent it by a single byte in > memory which can be discarded when reading or writing. Another > alternative is to not provide a Storable instance for it at all. Let > the users write their own if they need it. > > If you think this does not have a problem, can you suggest how to > elegantly handle the array implementation problem as I described > above? > > Thanks, > Harendra > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ky3 at atamo.com Wed Jan 5 14:36:16 2022 From: ky3 at atamo.com (Kim-Ee Yeoh) Date: Wed, 5 Jan 2022 22:36:16 +0800 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: On Wed, Jan 5, 2022 at 7:10 PM Harendra Kumar wrote: > It is hard to objectively or mathematically prove which option is > better. You’ve made good points in your posts. Worth to keep in mind that the number of bits needed to distinguish between n things is log2 n. The log of 1 is 0. > -- -- Kim-Ee -------------- next part -------------- An HTML attachment was scrubbed... URL: From harendra.kumar at gmail.com Wed Jan 5 14:38:23 2022 From: harendra.kumar at gmail.com (Harendra Kumar) Date: Wed, 5 Jan 2022 20:08:23 +0530 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: On Wed, 5 Jan 2022 at 19:22, Fumiaki Kinoshita wrote: > Perhaps you may want to reconsider the design of your array implementation before roasting this instance as "broken" and "absurd". Fumiaki, thanks for the pointers to the origin of the instance. My intention was not to roast, but just to have a discussion to find out the reasoning in favour and against. I am sorry if it sounded like roasting. -harendra From harendra.kumar at gmail.com Wed Jan 5 14:51:11 2022 From: harendra.kumar at gmail.com (Harendra Kumar) Date: Wed, 5 Jan 2022 20:21:11 +0530 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: On Wed, 5 Jan 2022 at 20:06, Kim-Ee Yeoh wrote: > You’ve made good points in your posts. > > Worth to keep in mind that the number of bits needed to distinguish between n things is log2 n. > > The log of 1 is 0. Exactly. And that is why we should not have a Storable instance for (). -harendra From ky3 at atamo.com Wed Jan 5 15:23:48 2022 From: ky3 at atamo.com (Kim-Ee Yeoh) Date: Wed, 5 Jan 2022 22:23:48 +0700 Subject: Storable instance of () is broken In-Reply-To: References: Message-ID: One way to make rapid progress is to adopt the approach that Henning took. Is Storable that large and/or so frequently updated a library that you cannot create your own variant of it? On Wed, Jan 5, 2022 at 9:51 PM Harendra Kumar wrote: > On Wed, 5 Jan 2022 at 20:06, Kim-Ee Yeoh wrote: > > You’ve made good points in your posts. > > > > Worth to keep in mind that the number of bits needed to distinguish > between n things is log2 n. > > > > The log of 1 is 0. > > Exactly. And that is why we should not have a Storable instance for (). > > -harendra > -- -- Kim-Ee -------------- next part -------------- An HTML attachment was scrubbed... URL: From hecate at glitchbra.in Sun Jan 9 16:17:29 2022 From: hecate at glitchbra.in (=?UTF-8?Q?H=c3=a9cate?=) Date: Sun, 9 Jan 2022 17:17:29 +0100 Subject: Takeover of cryptonite and memory Message-ID: <212a1234-8db9-1a7b-7e21-8baca886efe0@glitchbra.in> Hello Hackage admins, Several board members of the Haskell Foundation have tried for many months to contact Vincent Hanquez regarding his cryptography packages, that have become cornerstones of the ecosystem and of industrial Haskell products. Recently, I have received complaints and questions about those packages being major blockers for the adoption of newer GHC versions. I have thus made another home for those packages with the explicit aim of diversifying the ownership and maintenance so that we do not have such a high bus factor. I would like to start a Hackage takeover procedure for the following packages: * cryptonite * memory Their new home would be the haskell-cryptography GitHub organisation, and emails have been sent to past contributors, in order to maintain continuity in the development teams. My Hackage username is "hecate". Chris Dornan (in CC) is also involved, and his username is "ChrisDornan". Moreover, as a board member of the Haskell Foundation, I intend to provide resources to help coordination between the stakeholders of these packages, in the community and industry. We will be at your disposal for any question. Cheers. Hécate ✨ 🐦: @TechnoEmpress IRC: Hecate WWW: https://glitchbra.in RUN: BSD From andreas.abel at ifi.lmu.de Fri Jan 14 07:05:29 2022 From: andreas.abel at ifi.lmu.de (Andreas Abel) Date: Fri, 14 Jan 2022 08:05:29 +0100 Subject: Proposal: export liftA2 from Prelude Message-ID: Proposal: export liftA2 from Prelude. More information and discussion here: https://gitlab.haskell.org/ghc/ghc/-/issues/20945 Discussion period 2 weeks TL;DR - Fixes weird qualification in warning • No explicit implementation for either ‘<*>’ or ‘GHC.Base.liftA2’ • In the instance declaration for ‘Applicative ’ - Was considered for GHC 8.2 but never followed up From david.feuer at gmail.com Fri Jan 14 13:43:16 2022 From: david.feuer at gmail.com (David Feuer) Date: Fri, 14 Jan 2022 08:43:16 -0500 Subject: Proposal: export liftA2 from Prelude In-Reply-To: References: Message-ID: I'm very much in favor (as will surprise no one), but there's a new proposal process. Open an issue at https://github.com/haskell/core-libraries-committee/issues On Fri, Jan 14, 2022, 2:06 AM Andreas Abel wrote: > Proposal: export liftA2 from Prelude. > More information and discussion here: > > https://gitlab.haskell.org/ghc/ghc/-/issues/20945 > > Discussion period 2 weeks > > TL;DR > > - Fixes weird qualification in warning > > • No explicit implementation for > either ‘<*>’ or ‘GHC.Base.liftA2’ > • In the instance declaration for ‘Applicative ’ > > - Was considered for GHC 8.2 but never followed up > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From carter.schonwald at gmail.com Thu Jan 20 20:49:14 2022 From: carter.schonwald at gmail.com (Carter Schonwald) Date: Thu, 20 Jan 2022 15:49:14 -0500 Subject: whither is an MTL that supports transformers >= 0.6? Message-ID: Hey All, I was looking around and noticed we still lack that ... anyone have decent visibility ? -Carter -------------- next part -------------- An HTML attachment was scrubbed... URL: From ky3 at atamo.com Fri Jan 21 06:15:51 2022 From: ky3 at atamo.com (Kim-Ee Yeoh) Date: Fri, 21 Jan 2022 13:15:51 +0700 Subject: whither is an MTL that supports transformers >= 0.6? In-Reply-To: References: Message-ID: I don’t know. But I do know “whither” is used incorrectly in the email subject. The word isn’t equivalent to “where”, which is what you should’ve used. On Fri, Jan 21, 2022 at 3:49 AM Carter Schonwald wrote: > Hey All, > I was looking around and noticed we still lack that ... anyone have decent > visibility ? > > -Carter > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -- -- Kim-Ee -------------- next part -------------- An HTML attachment was scrubbed... URL: From carter.schonwald at gmail.com Fri Jan 21 15:05:00 2022 From: carter.schonwald at gmail.com (Carter Schonwald) Date: Fri, 21 Jan 2022 10:05:00 -0500 Subject: whither is an MTL that supports transformers >= 0.6? In-Reply-To: References: Message-ID: :) also the whither/whitherable lib is pretty nice :) On Fri, Jan 21, 2022 at 1:16 AM Kim-Ee Yeoh wrote: > I don’t know. But I do know “whither” is used incorrectly in the email > subject. The word isn’t equivalent to “where”, which is what you should’ve > used. > > On Fri, Jan 21, 2022 at 3:49 AM Carter Schonwald < > carter.schonwald at gmail.com> wrote: > >> Hey All, >> I was looking around and noticed we still lack that ... anyone have >> decent visibility ? >> >> -Carter >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > -- > -- Kim-Ee > -------------- next part -------------- An HTML attachment was scrubbed... URL: From josephcsible at gmail.com Fri Jan 28 01:25:04 2022 From: josephcsible at gmail.com (Joseph C. Sible) Date: Thu, 27 Jan 2022 18:25:04 -0700 Subject: Adding a "Map k a -> Set (Arg k a)" function to containers Message-ID: I opened https://github.com/haskell/containers/issues/814 about adding this function to the containers package: import qualified Data.Map.Internal as Map import qualified Data.Set.Internal as Set import Data.Semigroup (Arg(..)) mapToArgSet :: Map.Map k a -> Set.Set (Arg k a) mapToArgSet Map.Tip = Set.Tip mapToArgSet (Map.Bin sz k v l r) = Set.Bin sz (Arg k v) (mapToArgSet l) (mapToArgSet r) Does anyone have any suggestions or improvements for this? In particular, I'm sure there's a less clunky name that I just can't think of. Thanks, Joseph C. Sible From david.feuer at gmail.com Fri Jan 28 01:34:15 2022 From: david.feuer at gmail.com (David Feuer) Date: Thu, 27 Jan 2022 20:34:15 -0500 Subject: Adding a "Map k a -> Set (Arg k a)" function to containers In-Reply-To: References: Message-ID: I have one minor operational concern. Set is strict in its keys, so code doesn't have to check whether a key is evaluated when case matching on it. Arg, on the other hand, is lazy in both arguments. So this function throws away evaluatedness information, which is always a bit sad. Separately, I'd love see: 1. A function going the other way. 2. Unsafe mapMonotonic-style functions generalizing both. On Thu, Jan 27, 2022, 8:25 PM Joseph C. Sible wrote: > I opened https://github.com/haskell/containers/issues/814 about adding > this function to the containers package: > > import qualified Data.Map.Internal as Map > import qualified Data.Set.Internal as Set > import Data.Semigroup (Arg(..)) > > mapToArgSet :: Map.Map k a -> Set.Set (Arg k a) > mapToArgSet Map.Tip = Set.Tip > mapToArgSet (Map.Bin sz k v l r) = Set.Bin sz (Arg k v) (mapToArgSet > l) (mapToArgSet r) > > Does anyone have any suggestions or improvements for this? In > particular, I'm sure there's a less clunky name that I just can't > think of. > > Thanks, > > Joseph C. Sible > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.feuer at gmail.com Fri Jan 28 01:35:00 2022 From: david.feuer at gmail.com (David Feuer) Date: Thu, 27 Jan 2022 20:35:00 -0500 Subject: Adding a "Map k a -> Set (Arg k a)" function to containers In-Reply-To: References: Message-ID: Sorry, I meant *Map* is strict in its keys. Set is too, but that's not relevant to my concern. On Thu, Jan 27, 2022, 8:34 PM David Feuer wrote: > I have one minor operational concern. Set is strict in its keys, so code > doesn't have to check whether a key is evaluated when case matching on it. > Arg, on the other hand, is lazy in both arguments. So this function throws > away evaluatedness information, which is always a bit sad. > > Separately, I'd love see: > > 1. A function going the other way. > 2. Unsafe mapMonotonic-style functions generalizing both. > > On Thu, Jan 27, 2022, 8:25 PM Joseph C. Sible > wrote: > >> I opened https://github.com/haskell/containers/issues/814 about adding >> this function to the containers package: >> >> import qualified Data.Map.Internal as Map >> import qualified Data.Set.Internal as Set >> import Data.Semigroup (Arg(..)) >> >> mapToArgSet :: Map.Map k a -> Set.Set (Arg k a) >> mapToArgSet Map.Tip = Set.Tip >> mapToArgSet (Map.Bin sz k v l r) = Set.Bin sz (Arg k v) (mapToArgSet >> l) (mapToArgSet r) >> >> Does anyone have any suggestions or improvements for this? In >> particular, I'm sure there's a less clunky name that I just can't >> think of. >> >> Thanks, >> >> Joseph C. Sible >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ietf-dane at dukhovni.org Sun Jan 30 00:25:16 2022 From: ietf-dane at dukhovni.org (Viktor Dukhovni) Date: Sat, 29 Jan 2022 19:25:16 -0500 Subject: Faster `elem` "Hack" for Data.Set? Message-ID: In a recent "Folding the unfoldable" "gist": https://oleg.fi/gists/posts/2022-01-25-folding-unfoldable.html Oleg describes a way to make unboxed Vectors Foldable: There is another way to make Foldable work, with a data Hack a b where Hack :: U.Vector a -> Hack a a This is a two type-parameter wrapper, but the types are always the same! (I wish that could be a newtype). The Foldable instance is simply: instance U.Unbox a => Foldable (Hack a) where foldr f z (Hack v) = U.foldr f z v foldl' f z (Hack v) = U.foldl' f z v In the associated Reddit thread https://www.reddit.com/r/haskell/comments/sd6gel/comment/hudmsis/?utm_source=share&utm_medium=web2x&context=3 it was observed that a similar approach can be used to give Data.Set a performant `elem` method (code copied below my signature). It is somewhat tempting to consider whether the "Hack" ought to be built-in directly into the real Data.Set: https://www.reddit.com/r/haskell/comments/sd6gel/comment/huil070/?utm_source=share&utm_medium=web2x&context=3 type Set a = SetImpl a a data SetImpl a b where Bin :: {-# UNPACK #-} !Size -> !a -> !(SetImpl a) -> !(SetImpl a) -> SetImpl a a Tip :: SetImpl a a type Size = Int instance Ord a => Foldable (SetImpl a) where ... elem _ Tip = False elem x (Bin _ y l r) = case compare x y of EQ -> True LT -> elem x l GT -> elem x r ... This representation does not seem to carry any obvious runtime overhead, and gives Set a performant `elem` method. The only change from the status quo would be that folds would not be available for empty or singleton sets with non-Ord elements (currently possible with Set). How much of a loss would it be to constrain the element type of empty and singleton sets used in folds? Are there other issue that make the above impractical? -- Viktor. type OSet a = OrdSet a a data OrdSet a b where OSet :: Set.Set a -> OrdSet a a instance Ord a => Foldable (OrdSet a) where fold (OSet xs) = fold xs foldMap f (OSet xs) = foldMap f xs foldMap' f (OSet xs) = foldMap' f xs foldr f z (OSet xs) = foldr f z xs foldr' f z (OSet xs) = foldr' f z xs foldl f z (OSet xs) = foldl f z xs foldl' f z (OSet xs) = foldl' f z xs foldr1 f (OSet xs) = foldr1 f xs foldl1 f (OSet xs) = foldl1 f xs toList (OSet xs) = toList xs null (OSet xs) = null xs length (OSet xs) = length xs -- The point of the exercise is `elem` elem e (OSet xs) = Set.member e xs maximum (OSet xs) = maximum xs minimum (OSet xs) = minimum xs sum (OSet xs) = sum xs product (OSet xs) = product xs instance Eq a => Eq (OSet a) where (OSet xs) == (OSet ys) = xs == ys instance (Eq a, Ord a) => Ord (OSet a) where compare (OSet xs) (OSet ys) = compare xs ys empty :: Ord a => OSet a empty = OSet Set.empty singleton :: Ord a => a -> OSet a singleton = OSet . Set.singleton fromList :: Ord a => [a] -> OSet a fromList = OSet . Set.fromList (\\) :: Ord a => OSet a -> OSet a -> OSet a (OSet xs) \\ (OSet ys) = OSet (xs Set.\\ ys) alterF :: (Ord a, Functor f) => (Bool -> f Bool) -> a -> OSet a -> f (OSet a) alterF f x (OSet xs) = fmap OSet $ Set.alterF f x xs ... From david.feuer at gmail.com Sun Jan 30 00:59:51 2022 From: david.feuer at gmail.com (David Feuer) Date: Sat, 29 Jan 2022 19:59:51 -0500 Subject: Faster `elem` "Hack" for Data.Set? In-Reply-To: References: Message-ID: The tighter `Foldable` instance probably wouldn't hurt too much in practice. I have some other concerns, however. One is that `containers` has a long history of attempting to remain compatible with potential future Haskell 98-like Haskell implementations (likely with a few days of work to patch up mistakes when the time comes). Using a GADT to define Set would break that quite thoroughly. My second concern is more practical: type Set a = SetImpl a a is all sorts of problematic. It forces anyone who wants to use what's always been the `Set` type constructor (Set :: Type -> Type) to dig into the implementation and use `SetImpl :: Type -> Type -> Type` instead. That's quite a different beast. I don't know all the things that will break, but they'll surely include https://hackage.haskell.org/package/constrained-monads-0.5.0.0/docs/Control-Monad-Constrained.html . I think the real answer is to remove `elem` from `Foldable` and put it somewhere more appropriate. On Sat, Jan 29, 2022 at 7:26 PM Viktor Dukhovni wrote: > > > In a recent "Folding the unfoldable" "gist": > > https://oleg.fi/gists/posts/2022-01-25-folding-unfoldable.html > > Oleg describes a way to make unboxed Vectors Foldable: > > There is another way to make Foldable work, with a > > data Hack a b where > Hack :: U.Vector a -> Hack a a > > This is a two type-parameter wrapper, but the types are always the > same! (I wish that could be a newtype). The Foldable instance is > simply: > > instance U.Unbox a => Foldable (Hack a) where > foldr f z (Hack v) = U.foldr f z v > foldl' f z (Hack v) = U.foldl' f z v > > In the associated Reddit thread > > https://www.reddit.com/r/haskell/comments/sd6gel/comment/hudmsis/?utm_source=share&utm_medium=web2x&context=3 > > it was observed that a similar approach can be used to give Data.Set a > performant `elem` method (code copied below my signature). > > It is somewhat tempting to consider whether the "Hack" ought to be > built-in directly into the real Data.Set: > > https://www.reddit.com/r/haskell/comments/sd6gel/comment/huil070/?utm_source=share&utm_medium=web2x&context=3 > > type Set a = SetImpl a a > data SetImpl a b where > Bin :: {-# UNPACK #-} !Size -> !a -> !(SetImpl a) -> !(SetImpl a) -> SetImpl a a > Tip :: SetImpl a a > type Size = Int > > instance Ord a => Foldable (SetImpl a) where > ... > elem _ Tip = False > elem x (Bin _ y l r) = case compare x y of > EQ -> True > LT -> elem x l > GT -> elem x r > ... > > This representation does not seem to carry any obvious runtime overhead, > and gives Set a performant `elem` method. The only change from the > status quo would be that folds would not be available for empty or > singleton sets with non-Ord elements (currently possible with Set). > > How much of a loss would it be to constrain the element type of empty > and singleton sets used in folds? Are there other issue that make the > above impractical? > > -- > Viktor. > > type OSet a = OrdSet a a > data OrdSet a b where > OSet :: Set.Set a -> OrdSet a a > > instance Ord a => Foldable (OrdSet a) where > fold (OSet xs) = fold xs > foldMap f (OSet xs) = foldMap f xs > foldMap' f (OSet xs) = foldMap' f xs > foldr f z (OSet xs) = foldr f z xs > foldr' f z (OSet xs) = foldr' f z xs > foldl f z (OSet xs) = foldl f z xs > foldl' f z (OSet xs) = foldl' f z xs > foldr1 f (OSet xs) = foldr1 f xs > foldl1 f (OSet xs) = foldl1 f xs > toList (OSet xs) = toList xs > null (OSet xs) = null xs > length (OSet xs) = length xs > -- The point of the exercise is `elem` > elem e (OSet xs) = Set.member e xs > maximum (OSet xs) = maximum xs > minimum (OSet xs) = minimum xs > sum (OSet xs) = sum xs > product (OSet xs) = product xs > > instance Eq a => Eq (OSet a) where > (OSet xs) == (OSet ys) = xs == ys > > instance (Eq a, Ord a) => Ord (OSet a) where > compare (OSet xs) (OSet ys) = compare xs ys > > empty :: Ord a => OSet a > empty = OSet Set.empty > > singleton :: Ord a => a -> OSet a > singleton = OSet . Set.singleton > > fromList :: Ord a => [a] -> OSet a > fromList = OSet . Set.fromList > > (\\) :: Ord a => OSet a -> OSet a -> OSet a > (OSet xs) \\ (OSet ys) = OSet (xs Set.\\ ys) > > alterF :: (Ord a, Functor f) => (Bool -> f Bool) -> a -> OSet a -> f (OSet a) > alterF f x (OSet xs) = fmap OSet $ Set.alterF f x xs > > ... > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries From pho at cielonegro.org Sun Jan 30 05:11:13 2022 From: pho at cielonegro.org (PHO) Date: Sun, 30 Jan 2022 14:11:13 +0900 Subject: Faster `elem` "Hack" for Data.Set? In-Reply-To: References: Message-ID: <4e143b44-03fd-9b39-633e-bfdd19c3c348@cielonegro.org> On 1/30/22 9:59 AM, David Feuer wrote: [snip] > . I think the real answer is to remove `elem` from `Foldable` and put > it somewhere more appropriate. In a separate class? That would only help `elem` but not any other methods of Foldable. I'd rather love to see MonoFoldable[1] to be moved to base then. [1]: https://hackage.haskell.org/package/mono-traversable-1.0.15.3/docs/Data-MonoTraversable.html#t:MonoFoldable From david.feuer at gmail.com Sun Jan 30 05:19:46 2022 From: david.feuer at gmail.com (David Feuer) Date: Sun, 30 Jan 2022 00:19:46 -0500 Subject: Faster `elem` "Hack" for Data.Set? In-Reply-To: <4e143b44-03fd-9b39-633e-bfdd19c3c348@cielonegro.org> References: <4e143b44-03fd-9b39-633e-bfdd19c3c348@cielonegro.org> Message-ID: MonoFoldable isn't the answer either. That also has the problem of requiring the same constraint for folding as for membership tests. On Sun, Jan 30, 2022, 12:12 AM PHO wrote: > On 1/30/22 9:59 AM, David Feuer wrote: > [snip] > > . I think the real answer is to remove `elem` from `Foldable` and put > > it somewhere more appropriate. > > In a separate class? That would only help `elem` but not any other > methods of Foldable. I'd rather love to see MonoFoldable[1] to be moved > to base then. > > [1]: > > https://hackage.haskell.org/package/mono-traversable-1.0.15.3/docs/Data-MonoTraversable.html#t:MonoFoldable > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk Sun Jan 30 08:54:35 2022 From: tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk (Tom Ellis) Date: Sun, 30 Jan 2022 08:54:35 +0000 Subject: Faster `elem` "Hack" for Data.Set? In-Reply-To: References: Message-ID: On Sat, Jan 29, 2022 at 07:25:16PM -0500, Viktor Dukhovni wrote: > it was observed that a similar approach can be used to give Data.Set a > performant `elem` method But why? It is impossible to program polymorphically in a meaningful way with Foldable, because Foldable has no coherent operational semantics. We discussed this at: https://github.com/haskell/core-libraries-committee/issues/20 If we could "improve" Set's `elem` instance what would that gain us? That we could write a Foldable-polymorphic function that works well on Set and badly on []? Then wy not just specialise to Set in the first place? Trying to "improve" Foldable instances seems like a fool's errand to me, especially if it trades off simplicity somewhere else. Tom From keith.wygant at gmail.com Sun Jan 30 14:30:53 2022 From: keith.wygant at gmail.com (Keith) Date: Sun, 30 Jan 2022 14:30:53 +0000 Subject: Faster `elem` "Hack" for Data.Set? In-Reply-To: References: <4e143b44-03fd-9b39-633e-bfdd19c3c348@cielonegro.org> Message-ID: <0C9D5BCD-3D92-45ED-A7C5-167A67E30D53@gmail.com> I haven't found a downside to requiring that the elements of sets be `Ord` to be `MonoFoldable`. Yes, the dictionary is ignored by all the generic methods, but you still needed it to construct the `Set`. `MonoFoldable` includes some questionable design choices, but the `Ord` requirement for `Set` is not one. Sent from my phone with K-9 Mail. On 30 January 2022 05:19:46 UTC, David Feuer wrote: >MonoFoldable isn't the answer either. That also has the problem of >requiring the same constraint for folding as for membership tests. > >On Sun, Jan 30, 2022, 12:12 AM PHO wrote: > >> On 1/30/22 9:59 AM, David Feuer wrote: >> [snip] >> > . I think the real answer is to remove `elem` from `Foldable` and put >> > it somewhere more appropriate. >> >> In a separate class? That would only help `elem` but not any other >> methods of Foldable. I'd rather love to see MonoFoldable[1] to be moved >> to base then. >> >> [1]: >> >> https://hackage.haskell.org/package/mono-traversable-1.0.15.3/docs/Data-MonoTraversable.html#t:MonoFoldable >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From ietf-dane at dukhovni.org Sun Jan 30 16:07:37 2022 From: ietf-dane at dukhovni.org (Viktor Dukhovni) Date: Sun, 30 Jan 2022 11:07:37 -0500 Subject: Faster `elem` "Hack" for Data.Set? In-Reply-To: References: Message-ID: On Sat, Jan 29, 2022 at 07:59:51PM -0500, David Feuer wrote: > One is that `containers` has a long history of attempting to remain > compatible with potential future Haskell 98-like Haskell > implementations (likely with a few days of work to patch up mistakes > when the time comes). Using a GADT to define Set would break that > quite thoroughly. Yes, that's definitely an issue. > My second concern is more practical: > > type Set a = SetImpl a a > > is all sorts of problematic. It forces anyone who wants to use what's > always been the `Set` type constructor (Set :: Type -> Type) to dig > into the implementation and use `SetImpl :: Type -> Type -> Type` > instead. That's quite a different beast. Well, they could use the unsaturated type alias in some cases, but there are likely situations where that's not an option. > I don't know all the things that will break, but they'll surely > include > . Yes, that would break irreperably > I think the real answer is to remove `elem` from `Foldable` and put it > somewhere more appropriate. Perhaps so, though my guess is that this is unlikely to happen... Overall, your objections are likely sufficient. -- Viktor.