Implementing fixed-sized vectors (using datatype algebra?)

Hi, The EDSL implementation (system design) I'm working on would really benefit from an implementation of fixed-sized vectors. I thought this would be a generally desired ADT but it turned out I wasn't able to find an implementation. I was thinking about using datatype algebra plus GADTs to implement a type-level parameter indicating the size of the vector. I'm a total noob with regard to GADTs and type-level algebra trickery. So my questions are: 1) Do you think it is feasible? Can you think about a better approach? 2) An implementation of type-level Naturals would really help. What has already been done? Thanks in advance, Fons

On Thu, 31 Jan 2008, Alfonso Acosta wrote:
The EDSL implementation (system design) I'm working on would really benefit from an implementation of fixed-sized vectors.
I thought this would be a generally desired ADT but it turned out I wasn't able to find an implementation.
I was thinking about using datatype algebra plus GADTs to implement a type-level parameter indicating the size of the vector.
http://www.haskell.org/haskellwiki/Linear_algebra http://www.haskell.org/haskellwiki/Libraries_and_tools/Mathematics#Linear_al... "Frederik Eaton's library for statically checked matrix manipulation in Haskell"

Am Donnerstag, 31. Januar 2008 12:02 schrieb Alfonso Acosta:
Hi,
The EDSL implementation (system design) I'm working on would really benefit from an implementation of fixed-sized vectors.
I thought this would be a generally desired ADT but it turned out I wasn't able to find an implementation.
I was thinking about using datatype algebra plus GADTs to implement a type-level parameter indicating the size of the vector.
I'm a total noob with regard to GADTs and type-level algebra trickery. So my questions are:
1) Do you think it is feasible? Can you think about a better approach?
2) An implementation of type-level Naturals would really help. What has already been done?
Thanks in advance,
Fons
Hello Fons, interestingly, it occured to me yesterday that the graphics part of Grapefruit would benefit from fixed sized vectors. I think we should implement some small Cabal package which just provides this and upload it to the HackageDB. Are you interested in cooperating with me on this? Best wishes, Wolfgang

On Jan 31, 2008 3:03 PM, Wolfgang Jeltsch
Hello Fons,
interestingly, it occured to me yesterday that the graphics part of Grapefruit would benefit from fixed sized vectors. I think we should implement some small Cabal package which just provides this and upload it to the HackageDB. Are you interested in cooperating with me on this?
Sure I am! Actually, thanks to the pointers provided by Henning I learned that Oleg (who else!) already has implemented them. See http://okmij.org/ftp/Haskell/number-parameterized-types.html http://www.haskell.org/tmrwiki/NumberParamTypes http://okmij.org/ftp/Haskell/number-param-vector-code.tar.gz (Oleg's multiple flavour implementations of fixed-sized vectors) I think we should base our implementation on Oleg's (for which we need his permission). Actually, I think we should create two separate libraries. One for decimal type-level arithmetic and another for the vector implementation itself. What do you think?

On Thu, 31 Jan 2008, Alfonso Acosta wrote:
On Jan 31, 2008 3:03 PM, Wolfgang Jeltsch
wrote: Hello Fons,
interestingly, it occured to me yesterday that the graphics part of Grapefruit would benefit from fixed sized vectors. I think we should implement some small Cabal package which just provides this and upload it to the HackageDB. Are you interested in cooperating with me on this?
Sure I am!
Actually, thanks to the pointers provided by Henning I learned that Oleg (who else!) already has implemented them. See
http://okmij.org/ftp/Haskell/number-parameterized-types.html http://www.haskell.org/tmrwiki/NumberParamTypes http://okmij.org/ftp/Haskell/number-param-vector-code.tar.gz (Oleg's multiple flavour implementations of fixed-sized vectors)
I think we should base our implementation on Oleg's (for which we need his permission). Actually, I think we should create two separate libraries. One for decimal type-level arithmetic
I remember that type-level arithmetic is already implemented somewhere, certainly more than once, but certainly seldom in a nicely packaged form. erm, here http://www.haskell.org/haskellwiki/Type_arithmetic http://www.haskell.org/ghc/docs/latest/html/libraries/base/Data-Fixed.html#t... also here: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/numbers-2007.9.25 ?

Hello Henning, Thursday, January 31, 2008, 5:49:23 PM, you wrote:
I remember that type-level arithmetic is already implemented somewhere, certainly more than once, but certainly seldom in a nicely packaged form.
one more: darcs get --partial --tag '0.1' http://www.eecs.tufts.edu/~rdocki01/typenats/ -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On Jan 31, 2008 5:47 PM, Bulat Ziganshin
one more: darcs get --partial --tag '0.1' http://www.eecs.tufts.edu/~rdocki01/typenats/
Thanks for the link, I had already checked this library, but using a binary representation has the same problem as using peano numbers, error reports can be quite cryptic. I still think that using decimals is the way to go.

I remember that type-level arithmetic is already implemented somewhere, certainly more than once, but certainly seldom in a nicely packaged form.
erm, here http://www.haskell.org/haskellwiki/Type_arithmetic
Yep, there seem to be a few implementations around (decimal, binary, peano) but Oleg's decimal one is likely to be the most friendly when it comes to compiler errors etc ..
http://www.haskell.org/ghc/docs/latest/html/libraries/base/Data-Fixed.html#t...
also here: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/numbers-2007.9.25 ?
I'm probably missing something, but I don't understand how these libraries could help. Thanks, Fons

Am Donnerstag, 31. Januar 2008 15:15 schrieben Sie:
[…]
I think we should base our implementation on Oleg's (for which we need his permission).
Do you know whether Oleg has released his code under an open source license? If he had, we wouldn’t need his permission. I’m not sure whether we need his permission if we implement a library from scratch, using the concepts behind his library. Concerning the type-indexed lists, we possibly should use the GADT approach instead of the “thrusted core” approach. Concerning type-level naturals, we might want to use the representation () :- D1 :- D2 :- D9 (see my other e-mail).
Actually, I think we should create two separate libraries. One for decimal type-level arithmetic and another for the vector implementation itself.
Definitely!
[…]
Best wishes, Wolfgang


Am Donnerstag, 31. Januar 2008 18:30 schrieb Dominic Steinitz:
Look at
This is essentially what I had in mind. While Oleg’s implementation needs a “thrusted core”, the GADT solution doesn’t. It would be interesting to combine GADTs with decimal type-level naturals. This could look like this:
class Succ val succ | val -> succ, succ -> val where
[…]
data List len elem where
Nil :: List Sz elem
Cons :: (Succ len len') => elem -> List len elem -> List len' elem
Or with efficient contcatenation:
class Sum val1 val2 sum | val1 val2 -> sum, val1 sum -> val2, val2 sum -> val1 where
[…]
data List len elem where
Empty :: List Sz elem
Singleton :: List (D1 Sz) elem
Append :: (Add len1 len2 len') => List len1 elem -> List len2 elem -> List len' elem
Note, that type families might make this code even nicer. Some words on the representation of decimal numbers as types. While the representation with types of the form D1 (D2 (D3 Sz)) has the advantage of allowing numbers of arbitrary size, it has the disadvantage of a growing number of parantheses. In my opinion, it would be nicer to have somethink like D1 :- D2 :- D9 :- () with a right-associative operator :-. We could even build the digit list the other way round—() :- D1 :- D2 :- D9—using a left-associative :-. With the latter representation, we wouldn’t need to reverse digit sequences when adding numbers. Well, the representation (D1,D2,D9) might be considered more readable. It has the disadvantage of a fixed maximum size for the numbers. Which takes me to a point I had already considered some time ago: Wouldn’t it be good if we had just a type data Pair val1 val2 = Pair val1 val2 and if then (val1,val2,…,valn) would just be syntactic sugar for this: val1 `Pair` (val2 `Pair` (…(valn `Pair` ())…)) Best wishes, Wolfgang

Wolfgang Jeltsch wrote:
Well, the representation (D1,D2,D9) might be considered more readable. It has the disadvantage of a fixed maximum size for the numbers. Which takes me to a point I had already considered some time ago: Wouldn’t it be good if we had just a type
data Pair val1 val2 = Pair val1 val2
and if then (val1,val2,…,valn) would just be syntactic sugar for this:
val1 `Pair` (val2 `Pair` (…(valn `Pair` ())…))
I've thought of that too.. besides the asymmetry, the presence of _|_/seq makes them actually not equivalent though, unfortunately ~Isaac

Am Freitag, 1. Februar 2008 05:11 schrieben Sie:
Wolfgang Jeltsch wrote:
Well, the representation (D1,D2,D9) might be considered more readable. It has the disadvantage of a fixed maximum size for the numbers. Which takes me to a point I had already considered some time ago: Wouldn’t it be good if we had just a type
data Pair val1 val2 = Pair val1 val2
and if then (val1,val2,…,valn) would just be syntactic sugar for this:
val1 `Pair` (val2 `Pair` (…(valn `Pair` ())…))
I've thought of that too.. besides the asymmetry, the presence of _|_/seq makes them actually not equivalent though, unfortunately
~Isaac
With Ryan’s proposal (using strictness annotations) the new representation should be equivalent to the old one. Or am I missing something? Best wishes, Wolfgang

Wolfgang Jeltsch wrote:
Am Freitag, 1. Februar 2008 05:11 schrieben Sie:
Wolfgang Jeltsch wrote:
Well, the representation (D1,D2,D9) might be considered more readable. It has the disadvantage of a fixed maximum size for the numbers. Which takes me to a point I had already considered some time ago: Wouldn’t it be good if we had just a type
data Pair val1 val2 = Pair val1 val2
and if then (val1,val2,…,valn) would just be syntactic sugar for this:
val1 `Pair` (val2 `Pair` (…(valn `Pair` ())…)) I've thought of that too.. besides the asymmetry, the presence of _|_/seq makes them actually not equivalent though, unfortunately
~Isaac
With Ryan’s proposal (using strictness annotations) the new representation should be equivalent to the old one. Or am I missing something?
adding the strictness annotation seems to make them equivalent, yes I agree (I hadn't seen that post when I wrote that reply) ~Isaac

On Jan 31, 2008 11:35 PM, Wolfgang Jeltsch
Am Donnerstag, 31. Januar 2008 18:30 schrieb Dominic Steinitz:
Look at
This is essentially what I had in mind. While Oleg's implementation needs a "thrusted core", the GADT solution doesn't.
True. However using GADTs doesn't allow to internally make use of Arrays, which (tell me if I'm wrong) are likely to be faster than the naive GADT implementation. Actually the GADT implementation you proposed fits nicely with the Vector definition already used in my EDSL (it is isomorphic actually), but efficiency could be an issue.
Some words on the representation of decimal numbers as types. While the representation with types of the form D1 (D2 (D3 Sz)) has the advantage of allowing numbers of arbitrary size, it has the disadvantage of a growing number of parantheses. In my opinion, it would be nicer to have somethink like D1 :- D2 :- D9 :- () with a right-associative operator :-. We could even build the digit list the other way round—() :- D1 :- D2 :- D9—using a left-associative :-. With the latter representation, we wouldn't need to reverse digit sequences when adding numbers.
Right, I agree. I think we should use the arbitrary-size implementation (actually, how arbitrary is it? what's the limit of GHC, if any?). To make it friendlier for the end user I thought about defining aliases for lets say the first 10000 numbers using Template Haskell. That could even make error reports friendlier (not sure to what point though). What do you think? So, we'll be making two separate libraries then. We should think about names. What about FixedVector for the vector library and DecTypArith (maybe too long) or DecTypes for the type-level decimal arithmetic library? I'll put my hands dirty once we agree on this. Cheers, Fons

What about FixedVector for the vector library and DecTypArith (maybe too long) or DecTypes for the type-level decimal arithmetic library?
Actually it would maybe be better to create common high-level interface that could include unary, binary and decimal arithmetic so that the library could be easily reused in other projects (people like Bjorn, seem to be using the unary implementation). I don't know if it would be feasible though.

Alfonso Acosta
Actually it would maybe be better to create common high-level interface that could include unary, binary and decimal arithmetic so that the library could be easily reused in other projects (people like Bjorn, seem to be using the unary implementation). I don't know if it would be feasible though.
My reason for going with unary implementation was that it seemed to be the easiest and less tedious to implement. But is sure does make the error messages less friendly. It also limits the magnitude of the numbers quite severely unless one bumps up the type checker stack size. From the source code: "The practical size of the NumTypes is limited by the type checker stack. If the NumTypes grow too large (which can happen quickly with multiplication) an error message similar to the following will be emitted: Context reduction stack overflow; size = 20 Use -fcontext-stack=N to increase stack size to N This situation could concievably be mitigated significantly by using e.g. a binary representation of integers rather than Peano numbers." For my use (keeping track of physical dimensions) this hasn't been a problem. If Naturals had been sufficient for me I wouldn't have done my own implementation (I'm unaware of any other implementation of Integers). And there is certainly a lot of value to the clearer error messages from a decimal representation. Concerning whether or not there is any value in being able to change the representation of the type- level naturals in your library, my guess is that the value wouldn't be all that great. As I mentioned I use my implementation for tracking physical dimensions and HList's HNats for constraining vector dimensions but this isn't a problem since I cannot conceive of any reason why I would want to intermix the two. Of course, other usage scenarios exist where there would be a desire to intermix the vector dimensions with something else...? So sure, if you can abstract the representation away by all means do it. But I wouldn't go out of my way or impair the rest of the library to achieve this. Hope this helps, Bjorn

On 2008-02-01, Bjorn Buckwalter
If Naturals had been sufficient for me I wouldn't have done my own implementation (I'm unaware of any other implementation of Integers). And there is certainly a lot of value to the clearer error messages from a decimal representation.
I did a balanced-base-three (digits are 0, and +- 1) representation to get negative "decimals". Again, for a proof-of-concept dimensional analysis arithmetic. No problem with the stack, but the error messages are still less than clear. -- Aaron Denney -><-

On Fri, 1 Feb 2008, Aaron Denney wrote:
On 2008-02-01, Bjorn Buckwalter
wrote: If Naturals had been sufficient for me I wouldn't have done my own implementation (I'm unaware of any other implementation of Integers). And there is certainly a lot of value to the clearer error messages from a decimal representation.
I did a balanced-base-three (digits are 0, and +- 1) representation to get negative "decimals".
Nice. In German the digit values are sometimes called "eins, keins, meins". :-)

On Feb 5, 2008 4:10 PM, Henning Thielemann
On Fri, 1 Feb 2008, Aaron Denney wrote:
On 2008-02-01, Bjorn Buckwalter
wrote: If Naturals had been sufficient for me I wouldn't have done my own implementation (I'm unaware of any other implementation of Integers). And there is certainly a lot of value to the clearer error messages from a decimal representation.
I did a balanced-base-three (digits are 0, and +- 1) representation to get negative "decimals".
Nice. In German the digit values are sometimes called "eins, keins, meins". :-)
I'm almost done with the decimal library but it would be nice to check some Integer implementations for future inclusion. So, Aaron, Björn, are your implementations available somewhere?

On Feb 5, 2008 2:16 PM, Alfonso Acosta
On Feb 5, 2008 4:10 PM, Henning Thielemann
wrote: On Fri, 1 Feb 2008, Aaron Denney wrote:
On 2008-02-01, Bjorn Buckwalter
wrote: If Naturals had been sufficient for me I wouldn't have done my own implementation (I'm unaware of any other implementation of Integers). And there is certainly a lot of value to the clearer error messages from a decimal representation.
I did a balanced-base-three (digits are 0, and +- 1) representation to get negative "decimals".
Nice. In German the digit values are sometimes called "eins, keins, meins". :-)
I'm almost done with the decimal library but it would be nice to check some Integer implementations for future inclusion. So, Aaron, Björn, are your implementations available somewhere?
As noted elsewhere in the thread my implementation is available at: http://www.buckwalter.se/~bjorn/darcs/dimensional/Numeric/NumType.lhs It is my intent to extract this (self-contained) module to its own package and put on hackage. It's been a low priority for me but I'm rather incentivized by this thread. Thanks, Bjorn

On Feb 5, 2008 8:29 PM, Bjorn Buckwalter
On Feb 5, 2008 2:16 PM, Alfonso Acosta
wrote: On Feb 5, 2008 4:10 PM, Henning Thielemann
wrote: On Fri, 1 Feb 2008, Aaron Denney wrote:
On 2008-02-01, Bjorn Buckwalter
wrote: If Naturals had been sufficient for me I wouldn't have done my own implementation (I'm unaware of any other implementation of Integers). And there is certainly a lot of value to the clearer error messages from a decimal representation.
I did a balanced-base-three (digits are 0, and +- 1) representation to get negative "decimals".
Nice. In German the digit values are sometimes called "eins, keins, meins". :-)
I'm almost done with the decimal library but it would be nice to check some Integer implementations for future inclusion. So, Aaron, Björn, are your implementations available somewhere?
As noted elsewhere in the thread my implementation is available at:
http://www.buckwalter.se/~bjorn/darcs/dimensional/Numeric/NumType.lhs
Thanks!
It is my intent to extract this (self-contained) module to its own package and put on hackage. It's been a low priority for me but I'm rather incentivized by this thread.
Great! How about joining efforts? As I said I almost have a preliminary version of the decimal library which I'll realease for reviewing purpouses soon (It won't include Integer computations though)

I'm almost done with the decimal library but it would be nice to check some Integer implementations for future inclusion. So, Aaron, Björn, are your implementations available somewhere?
As noted elsewhere in the thread my implementation is available at:
http://www.buckwalter.se/~bjorn/darcs/dimensional/Numeric/NumType.lhs
Thanks!
It is my intent to extract this (self-contained) module to its own package and put on hackage. It's been a low priority for me but I'm rather incentivized by this thread.
Great!
How about joining efforts? As I said I almost have a preliminary version of the decimal library which I'll realease for reviewing purpouses soon (It won't include Integer computations though)
Well, could you elaborate a little on joining efforts? The effort I was planning to invest in my package consists mainly of creating a .cabal file plus some logistics to get tarballs to where they have to be. I understand that you (and Wolfgang) are creating a library of type level decimals for the purpose of constraining vector (list?) lengths. After that I haven't been paying attention fully to the thread. Is the goal to create a general-purpose library for type-level programming and my module would fit into that grander scheme? Or did you have something else in mind with joining efforts? E.g. help reviewing your code or writing new code?

On Feb 6, 2008 4:32 AM, Bjorn Buckwalter
Well, could you elaborate a little on joining efforts? The effort I was planning to invest in my package consists mainly of creating a .cabal file plus some logistics to get tarballs to where they have to be.
I understand that you (and Wolfgang) are creating a library of type level decimals for the purpose of constraining vector (list?) lengths. After that I haven't been paying attention fully to the thread. Is the goal to create a general-purpose library for type-level programming and my module would fit into that grander scheme?
Yes,the idea is to create a Cabal-ready wide-scope type-level programming library, joining the operations implemented in the different type-level libraries which are around. The goal (or at least mine) is to provide a common reusable type-level library which saves constantly reinventing the wheel. I'll provide an initial implementation (just including naturals in decimal representation) soon. Wolfgang suggested adding booleans at a later point too if I recall properly. Any useful type-level operation should have a place in the library.
Or did you have something else in mind with joining efforts? E.g. help reviewing your code or writing new code?
This would certainly help too.

On Feb 6, 2008 1:03 PM, Alfonso Acosta
On Feb 6, 2008 4:32 AM, Bjorn Buckwalter
wrote: I understand that you (and Wolfgang) are creating a library of type level decimals for the purpose of constraining vector (list?) lengths. After that I haven't been paying attention fully to the thread. Is the goal to create a general-purpose library for type-level programming and my module would fit into that grander scheme?
Yes,the idea is to create a Cabal-ready wide-scope type-level programming library, joining the operations implemented in the different type-level libraries which are around. The goal (or at least mine) is to provide a common reusable type-level library which saves constantly reinventing the wheel.
Ok. Is this what people want -- one big hold-all library with everything, as opposed to smaller more specialized packages? I guess I can see advantages (real or perceived) to both approaches. The other library I use for type-level programming is HList. It has type-level booleans already so you might what to take a look at it if you're not already familiar with it. In fact, if you are serious about creating the de facto(?) type-level programming library trying to get Oleg involved would be very beneficial both in terms of innovation and credibility.
Or did you have something else in mind with joining efforts? E.g. help reviewing your code or writing new code?
This would certainly help too.
I'm sure it would. ;) I didn't mean to imply that I have plenty of spare time to invest in this but I'll certainly be paying attention when you start releasing code. Thanks, Bjorn

On Feb 7, 2008 2:30 AM, Bjorn Buckwalter
Ok. Is this what people want -- one big hold-all library with everything, as opposed to smaller more specialized packages? I guess I can see advantages (real or perceived) to both approaches.
Apart from Dockins' typenats library there are no other user-friendly specific type-level libraries that know, so I cannot really tell if people would prefer a hold-all library or a couple of more granular specialized ones. Right now is not hold-all at all (it is still vaporware actually :)), so I think there's no reason to discuss that at this point. Let's see what people think.
The other library I use for type-level programming is HList. It has type-level booleans already so you might what to take a look at it if you're not already familiar with it.
Thanks I'll have a look at it.
In fact, if you are serious about creating the de facto(?) type-level programming library trying to get Oleg involved would be very beneficial both in terms of innovation and credibility.
Sure. I've actually been exchanging mail with Oleg. He has given me some useful suggestions and contributed with some code. He didn't mention to what point he wanted to get involved though, but I'm sure he will try to help.

Am Donnerstag, 7. Februar 2008 02:47 schrieb Alfonso Acosta:
The other library I use for type-level programming is HList. It has type-level booleans already so you might what to take a look at it if you're not already familiar with it.
Thanks I'll have a look at it.
I have to admit that I don’t like the names HBool, HTrue and HFalse. What do they mean? Heterogenous booleans? Heterogenous truth? Why it’s “Bool” instead of “Boolean” and therefore not conforming to the Prelude convention? Heterogenous lists are not directly about type level computation. A HList type is usually inhabited. On the other hand, types which denote type level naturals or type-level booleans are empty data types. So type level booleans should go into some type level programming library like the one Alfonso and I want to create. HList should then use these booleans. This is at least my opinion. Best wishes, Wolfgang

Don't expect anything astonishing yet, but an initial version of the
library can be found at
http:/code.haskell.org/type-level
To make reviewing easier I have put the haddock-generated documentation at
http://code.haskell.org/~fons/type-level/doc/
Some remarks:
* Only Positive and Natural numerals in decimal representation are
supported. It would be cool to add support for Integers though.
* The code is based on Oleg's implimentation of type-level binaries
http://okmij.org/ftp/Computation/resource-aware-prog/BinaryNumber.hs
* exponentiation/log and GCD is not implemented yet
* multiplication is not relational and thus division is broken. I
tried porting Oleg's multiplication algorithm without success.
* Aliases (in binary, octal decimal and hexadecimal form) for
type-level values and their value-level reflection functions are
generated with TH.
That implies:
* Long compilation time depending on your system
* Although errors will always be reported in decimal form, the end
user can input values using other bases (only for values in the range
of generated aliases of course)
* It would be cool to have "real" support for other bases apart from decimals
* It would imply having unlimited size of input for other bases
(right now if we want to input a value out of the alises limit,
decimal reprentation is mandatory)
* However, is it feasible? How could it be done without needing to
implement the operations for each base? WOuld it be possible to
"overload" the type-level operators so that they worked with different
representations and mixed representation arguments?
* Booleans are not implemented (Wolfgang?)
I would be happy to hear any suggestions, get code reviews and/or contributions.
Cheers,
Fons
On Feb 7, 2008 11:17 AM, Wolfgang Jeltsch
Am Donnerstag, 7. Februar 2008 02:47 schrieb Alfonso Acosta:
The other library I use for type-level programming is HList. It has type-level booleans already so you might what to take a look at it if you're not already familiar with it.
Thanks I'll have a look at it.
I have to admit that I don't like the names HBool, HTrue and HFalse. What do they mean? Heterogenous booleans? Heterogenous truth? Why it's "Bool" instead of "Boolean" and therefore not conforming to the Prelude convention?
Heterogenous lists are not directly about type level computation. A HList type is usually inhabited. On the other hand, types which denote type level naturals or type-level booleans are empty data types. So type level booleans should go into some type level programming library like the one Alfonso and I want to create. HList should then use these booleans. This is at least my opinion.
Best wishes, Wolfgang
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Am Donnerstag, 7. Februar 2008 15:56 schrieben Sie:
Don't expect anything astonishing yet, but an initial version of the library can be found at
http:/code.haskell.org/type-level
To make reviewing easier I have put the haddock-generated documentation at
Thanks for your effort. From a quick look at the Haddock documentation, some questions/remarks arised. Nat means “all natural numbers except zero” while Nat0 means “all natural numbers (including zero)”. Since in computer science, natural numbers usually cover zero, we should use Pos instead of Nat and Nat instead of Nat0. You seem to write 12 as 1 :+ 2 instead of () :+ 1 :+ 2. But I think, the latter representation should probably be prefered. With it, :+ always has a number as its left argument and a digit as its right. Without the () :+ we get ugly exceptional cases. You can see this, for example, in the instance declarations for Compare. With the second representation, we could reduce the number of instances dramatically. We would define a comparison of digits (verbose) and than a comparison of numbers based on the digit comparison (not verbose). :+ is already used as the constructor for complex numbers. We should probably use some different operator. Again, thanks for your work.
[…]
Best wishes, Wolfgang

On Feb 7, 2008 4:16 PM, Wolfgang Jeltsch
Nat means "all natural numbers except zero" while Nat0 means "all natural numbers (including zero)". Since in computer science, natural numbers usually cover zero, we should use Pos instead of Nat and Nat instead of Nat0.
Sounds sensible, actually Nat and Nat0 is confusing. However I would rather use Pos and Nat0 to make explicit that naturals include zero . Depending on the definition of naturals they might or might not include zero. http://en.wikipedia.org/wiki/Natural_number
You seem to write 12 as 1 :+ 2 instead of () :+ 1 :+ 2. But I think, the latter representation should probably be prefered. With it, :+ always has a number as its left argument and a digit as its right. Without the () :+ we get ugly exceptional cases. You can see this, for example, in the instance declarations for Compare. With the second representation, we could reduce the number of instances dramatically. We would define a comparison of digits (verbose) and than a comparison of numbers based on the digit comparison (not verbose).
Even if () would be preferred from the programmers point of view (I'm not sure how much we could reduce the number of instances though), it makes the representation less attractive on the user-side. Anyone using the library would find it annoying and would wonder why is it neccessary. Unless we find a way to use () only internally (we should use a one-character type to make it shorter to type) I think we should stick to current representation.
:+ is already used as the constructor for complex numbers. We should probably use some different operator.
Right. I didn't think about that, thanks.

I know that naming is arbitrary, but... Digits in types seems ugly to me. In this case, it is also redundant. Everyone but FORTRAN programmers counts from 0, not 1. Nat and Pos seem clear. Nat0 could even mean Nat \ {0}, the opposite of what is proposed, so confusion is even increased with Nat0. Dan Alfonso Acosta wrote:
On Feb 7, 2008 4:16 PM, Wolfgang Jeltsch
wrote: Nat means "all natural numbers except zero" while Nat0 means "all natural numbers (including zero)". Since in computer science, natural numbers usually cover zero, we should use Pos instead of Nat and Nat instead of Nat0.
Sounds sensible, actually Nat and Nat0 is confusing. However I would rather use Pos and Nat0 to make explicit that naturals include zero .
Depending on the definition of naturals they might or might not include zero. http://en.wikipedia.org/wiki/Natural_number
You seem to write 12 as 1 :+ 2 instead of () :+ 1 :+ 2. But I think, the latter representation should probably be prefered. With it, :+ always has a number as its left argument and a digit as its right. Without the () :+ we get ugly exceptional cases. You can see this, for example, in the instance declarations for Compare. With the second representation, we could reduce the number of instances dramatically. We would define a comparison of digits (verbose) and than a comparison of numbers based on the digit comparison (not verbose).
Even if () would be preferred from the programmers point of view (I'm not sure how much we could reduce the number of instances though), it makes the representation less attractive on the user-side. Anyone using the library would find it annoying and would wonder why is it neccessary.
Unless we find a way to use () only internally (we should use a one-character type to make it shorter to type) I think we should stick to current representation.
:+ is already used as the constructor for complex numbers. We should probably use some different operator.
Right. I didn't think about that, thanks. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Feb 7, 2008 8:38 PM, Dan Weston
I know that naming is arbitrary, but...
Digits in types seems ugly to me. In this case, it is also redundant. Everyone but FORTRAN programmers counts from 0, not 1. Nat and Pos seem clear. Nat0 could even mean Nat \ {0}, the opposite of what is proposed, so confusion is even increased with Nat0.
Ok, fair enough. I changed the names of Positives and Naturals (which do include 0) for Pos and Nat. The change (together with a connective rename from :+ to :* ) is already pushed in the darcs repository.

On 8 Feb 2008, at 8:38 am, Dan Weston wrote:
I know that naming is arbitrary, but...
Digits in types seems ugly to me. In this case, it is also redundant. Everyone but FORTRAN programmers counts from 0, not 1. Nat and Pos seem clear. Nat0 could even mean Nat \ {0}, the opposite of what is proposed, so confusion is even increased with Nat0.
For what it's worth, the Ada names for the types {x in Z | x >= 0} and {x in Z | x > 0} are "Natural" and "Positive" respectively. Natural is useful for counting stuff; Positive is useful mainly because Ada practice is to index from 1. In Z, |N is used for natural numbers, which are defined to include 0. In modern set theory, both the cardinal and the ordinal numbers start with 0, not with 1. All things considered, I would be very upset indeed if a type called "Nat" or "Natural" or something like that did not include 0.

Am Donnerstag, 7. Februar 2008 16:31 schrieben Sie:
On Feb 7, 2008 4:16 PM, Wolfgang Jeltsch
wrote: […]
You seem to write 12 as 1 :+ 2 instead of () :+ 1 :+ 2. But I think, the latter representation should probably be prefered. With it, :+ always has a number as its left argument and a digit as its right. Without the () :+ we get ugly exceptional cases. You can see this, for example, in the instance declarations for Compare. With the second representation, we could reduce the number of instances dramatically. We would define a comparison of digits (verbose) and than a comparison of numbers based on the digit comparison (not verbose).
Even if () would be preferred from the programmers point of view (I'm not sure how much we could reduce the number of instances though), it makes the representation less attractive on the user-side. Anyone using the library would find it annoying and would wonder why is it neccessary.
I wouldn’t wonder. Leaving out the () :* part just works because our type-level “values” are not typed, i.e., there aren’t different kinds Digit and Number but only kind *. If :+ would be a data constructor (on the value level), it would take a number and a digit argument which would forbid using a digit as its left argument. So I consider using a digit on the left as “unclean”. It’s similar to using a number as the second part of a cons cell in LISP.
[…]
Best wishes, Wolfgang

On Fri, 8 Feb 2008, Wolfgang Jeltsch wrote:
Am Donnerstag, 7. Februar 2008 16:31 schrieben Sie:
Even if () would be preferred from the programmers point of view (I'm not sure how much we could reduce the number of instances though), it makes the representation less attractive on the user-side. Anyone using the library would find it annoying and would wonder why is it neccessary.
I wouldn’t wonder. Leaving out the () :* part just works because our type-level “values” are not typed, i.e., there aren’t different kinds Digit and Number but only kind *. If :+ would be a data constructor (on the value level), it would take a number and a digit argument which would forbid using a digit as its left argument. So I consider using a digit on the left as “unclean”. It’s similar to using a number as the second part of a cons cell in LISP.
seconded

You seem to write 12 as 1 :+ 2 instead of () :+ 1 :+ 2. But I think, the latter representation should probably be prefered. With it, :+ always has a number as its left argument and a digit as its right. Without the () :+ we get ugly exceptional cases. You can see this, for example, in the instance declarations for Compare. With the second representation, we could reduce the number of instances dramatically. We would define a comparison of digits (verbose) and than a comparison of numbers based on the digit comparison (not verbose).
Even if () would be preferred from the programmers point of view (I'm not sure how much we could reduce the number of instances though), it makes the representation less attractive on the user-side. Anyone using the library would find it annoying and would wonder why is it neccessary.
I wouldn’t wonder. Leaving out the () :* part just works because our type-level “values” are not typed, i.e., there aren’t different kinds Digit and Number but only kind *. If :+ would be a data constructor (on the value level), it would take a number and a digit argument which would forbid using a digit as its left argument. So I consider using a digit on the left as “unclean”. It’s similar to using a number as the second part of a cons cell in LISP.
How 'bout treating :+ as similar to `append' rather than similar to `cons'? Basically treat :+ as taking 2 numbers (rather than a number and a digit). Stefan

On Feb 8, 2008, at 11:14 , Stefan Monnier wrote:
You seem to write 12 as 1 :+ 2 instead of () :+ 1 :+ 2. But I think, the latter representation should probably be prefered. (...) How 'bout treating :+ as similar to `append' rather than similar to `cons'? Basically treat :+ as taking 2 numbers (rather than a number and a digit).
Dumb questions department: why not define e.g. D'0 .. D'9 as () :* 0 .. () :* 9? Programmers then get D'1 :* 2, but the library sees () :* 1 :* 2. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On Fri, 8 Feb 2008, Brandon S. Allbery KF8NH wrote:
On Feb 8, 2008, at 11:14 , Stefan Monnier wrote:
You seem to write 12 as 1 :+ 2 instead of () :+ 1 :+ 2. But I think, the latter representation should probably be prefered. (...) How 'bout treating :+ as similar to `append' rather than similar to `cons'? Basically treat :+ as taking 2 numbers (rather than a number and a digit).
Dumb questions department: why not define e.g. D'0 .. D'9 as () :* 0 .. () :* 9? Programmers then get D'1 :* 2, but the library sees () :* 1 :* 2.
Do you remember that they talk about types D0, D1, and so on?

Moving on to the implementation of fixed-sized vectors themselves ... I have been trying to implement them as a GADT but I have run into quite few problems. As a result, I'm considering to implement them using the more-traditional phantom type-parameter approach. Anyhow, I'd like to share those problems with the list, just in case someone comes with a solution. Here are some examples of what I was able to define without problems (although, for some cases, I was forced to "break" the safety layer of the GADT by using the toInt reflection function). Save this email as FSVec.lhs to test them.
{-# LANGUAGE GADTs, Rank2Types, ScopedTypeVariables, KindSignatures #-} module Data.Param.FSVec where
import Data.TypeLevel.Num
The Fixed Sized Vector data type. I know Wolfgang would prefer something more closely named to LiSt to, but let's avoid getting into that discussion now.
data FSVec :: * -> * -> * where NullV :: FSVec D0 a (:>) :: Succ s s' => a -> FSVec s a -> FSVec s' a
infixr :>
Some successful examples
headV :: Pos s => FSVec s a -> a headV (x :> xs) = x
lastV :: Pos s => FSVec s a -> a lastV = lastV' -- trusted function without the Pos constraint, otherwise the compiler would complain about -- the Succ constraint of :> not being met. where lastV' :: FSVec s a -> a lastV' (x :> NullV) = x lastV' (x :> xs) = lastV' xs
atV :: (Pos s, Nat n, n :<: s) => FSVec s a -> n -> a atV v n = atV' v (toInt n) -- Reflecting the index breaks checking that the recursive call -- verifies the atV constraints, however I couldn't find another way. -- atV' is to be trusted regarding the recursive call where atV' :: FSVec s a -> Int -> a atV' (x :> xs) n | n == 0 = x | otherwise = atV' xs (n-1) -- this defition is nicer but doesn't typecheck -- atV (x :> xs) n -- | toInt n == 0 = x -- | otherwise = atV xs (predRef n)
Now some functions which I wasn't able to define Concat function. This would be the naive implementation, but it fails to compile. (<+>) :: Add s1 s2 s3 => FSVec s1 a -> FSVec s2 a -> FSVec s3 a NullV <+> ys = ys (x:>xs) <+> ys = x :> (xs <+> ys) Tail function, which is also incorrect. tailV :: Succ s' s => FSVec s a -> FSVec s' a tailV (x :> xs) = xs And finally, vector, which is supposed to build a fixed-sized vector out of a list. The ideal type for the function would be: vector :: [a] -> FSVec s a But there is no apparent way in which to obtain s based on the length of the input list. [1] shows a way in which to create vector using CPS style and a reification function: reifyInt :: Int -> (forall s . Nat s => FSVect s a -> w) -> w The result would be a function with the following type: vector :: [a] -> (forall s . Nat s => FSVec s a -> w) -> w Nevertheless, I'm not fully satisfied by it. Another alternative would be forcing the user to provide the size explicitly (which is ugly as well) vector' :: Nat s => s -> [a] -> FSVec s a The Succ constraint in the definition of :> doesn't allow me to do such a thing. The following implementation does not typecheck: vector' :: Nat s => s -> [a] -> FSVec s a vector' s l | toInt s == length l = vector' l | otherwise = error "dynamic/static size mismatch" where vector'' :: [a] -> FSVec s a vector'' [] = NullV vector'' (x : xs) = x :> vector' xs The problem is that I don't know a way in which to "bypass" the Succ constraint of :> . Using a traditional phantom type-parameter to express the size is the only solution I can think of (which would also solve the problems of init and tail). However, that would mean losing the ability of pattern matching with (:>). Any comments/suggestions would be very much appreciated. Cheers, Fons [1] http://ofb.net/~frederik/vectro/draft-r2.pdf

Am Sonntag, 10. Februar 2008 05:14 schrieben Sie:
[…]
Now some functions which I wasn't able to define
Concat function. This would be the naive implementation, but it fails to compile.
(<+>) :: Add s1 s2 s3 => FSVec s1 a -> FSVec s2 a -> FSVec s3 a NullV <+> ys = ys (x:>xs) <+> ys = x :> (xs <+> ys)
Hmm, we would have to find a way to implement lemmas. In this case, the lemma that succ (x + y) = succ x + y. At the moment, I’ve no good idea how to do that.
Tail function, which is also incorrect.
tailV :: Succ s' s => FSVec s a -> FSVec s' a tailV (x :> xs) = xs
Maybe this problem is similar to the one I came across earlier. See my mail on http://www.haskell.org/pipermail/haskell/2007-September/019757.html and the replies to it.
And finally, vector, which is supposed to build a fixed-sized vector out of a list.
The ideal type for the function would be:
vector :: [a] -> FSVec s a
But there is no apparent way in which to obtain s based on the length of the input list.
[1] shows a way in which to create vector using CPS style and a reification function:
reifyInt :: Int -> (forall s . Nat s => FSVect s a -> w) -> w
The result would be a function with the following type:
vector :: [a] -> (forall s . Nat s => FSVec s a -> w) -> w
Nevertheless, I'm not fully satisfied by it.
I suppose, it’s the best we can get without having dependent types. Others might correct me.
[…]
Some remark concerning spelling: Would it be possible to drop the V at the end of the function names? I think the fact that those functions are about “vectors” is better expressed by qualification: import Data.List as List import Data.TypeLevel.Vector as Vector -- Usage: Vector.last, List.last, … Compare this to the functions in Data.Map, Data.Set, etc. They also use insert, etc. instead of insertM, insertS, etc. Note that the latter would quickly lead to ambiguities because insertM would stand for map insertion as well as for multiset insertion. Similar with sets and sequences. Best wishes, Wolfgang

And finally, vector, which is supposed to build a fixed-sized vector out of a list.
The ideal type for the function would be:
vector :: [a] -> FSVec s a
But there is no apparent way in which to obtain s based on the length of the input list.
[1] shows a way in which to create vector using CPS style and a reification function:
reifyInt :: Int -> (forall s . Nat s => FSVect s a -> w) -> w
The result would be a function with the following type:
vector :: [a] -> (forall s . Nat s => FSVec s a -> w) -> w
Nevertheless, I'm not fully satisfied by it.
I suppose, it’s the best we can get without having dependent types. Others might correct me.
The type vector :: [a] -> FSVec s a doesn't make sense here: because s is universally quantified at the outside, this says "for all lengths s, given a list, I can produce a vector of length s". And indeed, in the second version, it looks like you're using the continuation to curry an existential: vector :: [a] -> (forall s . Nat s => FSVec s a -> w) -> w is the same as vector :: [a] -> ((exists s . Nat s and FSVec s a) -> w) -> w So why not just do vector :: [a] -> (exists s . Nat s and FSVec s a) I.e. data UnknownLengthVec a where U :: Nat s => FsVec s a -> UnknownLengthVec a vector :: [a] -> UnknownLengthVec a I haven't tried, but I'd be surprised if you couldn't write this function by recuring on the list. Of course, this type does not relate the size of the vector to the output of the length function on lists; it just says you get a vector some size. But maybe that's good enough for many uses? -Dan

Hi Dan,
On Feb 10, 2008 6:08 PM, Dan Licata
The ideal type for the function would be:
vector :: [a] -> FSVec s a
Well, I probably didn't express myself properly when writing "The ideal type", "the first type which comes to mind" would have been more accurate. Thanks for your explanation, which is actually much better than mine and, in fact, almost identical to the one included in http://ofb.net/~frederik/vectro/draft-r2.pdf

Hi Wolfgang,
On Feb 10, 2008 5:43 PM, Wolfgang Jeltsch
(<+>) :: Add s1 s2 s3 => FSVec s1 a -> FSVec s2 a -> FSVec s3 a -- line 78 NullV <+> ys = ys -- line 79 (x:>xs) <+> ys = x :> (xs <+> ys) -- line 80
Hmm, we would have to find a way to implement lemmas. In this case, the lemma that succ (x + y) = succ x + y. At the moment, I've no good idea how to do that.
I'm not sure if that is the only problem. I already mentioned I'm a noob with GADTs so I might be wrong but it seems that emulating dependent types with multiparameter type classes or type synonym families (more on this below) and GADT pattern matching does not work nicely. In the case of <+>, three errors are generated: 1) Couldn't match expected type `s3' against inferred type `s2' `s3' is a rigid type variable bound by the type signature for `<+>' at Data/Param/FSVec.hs:78:19 `s2' is a rigid type variable bound by the type signature for `<+>' at Data/Param/FSVec.hs:78:16 Expected type: FSVec s3 a Inferred type: FSVec s2 a 2) Could not deduce (Data.TypeLevel.Num.Sets.PosI s3, Data.TypeLevel.Num.Ops.NClassify s3 yc, Data.TypeLevel.Num.Ops.DivMod10 yh yl s3) from the context (Succ s11 s1) 3) Could not deduce (Data.TypeLevel.Num.Ops.Add' s11 s2 s, Data.TypeLevel.Num.Ops.Add' s2 s11 s) from the context (Succ s11 s1) arising from a use of `<+>' So defining the lema you mentioned (succ (x + y) = succ x + y) wouldn't solve the whole problem. Actually (2) wouldn't happen if the context of the function was taken into account (i.e. "from the context (Succ s11 s1)" should be "from the context (Succ s11 s1, Add s1 s2 s3)". Can someone explain why GHC does not behave like that? I don't know if type synonym families would help for <+> but certainly they don't for tailV:
tailV :: Succ s' s => FSVec s a -> FSVec s' a tailV (x :> xs) = xs
Maybe this problem is similar to the one I came across earlier. See my mail on http://www.haskell.org/pipermail/haskell/2007-September/019757.html and the replies to it.
As suggested by the pointer you provided, I redefined FSVec and tailV using a transformating of Succ into a type synonym family (see the end of this mail for its full definition) but it didn't help. data FSVec :: * -> * -> * where NullV :: FSVec D0 a (:>) :: FSVec s a -> FSVec (Succ s) a tailV :: FSVec s a -> FSVec (Succ s) a tailV (x :> xs) = xs tailV leads to this error (which I don't really understand :S) GADT pattern match in non-rigid context for `:>' Tell GHC HQ if you'd like this to unify the context In the pattern: x :> xs In the definition of `tailV': tailV (x :> xs) = xs My first impressions about using type synonym families (as opposed to multiparameter type classes) to fake dependent types are: * Positive part: - Operations on types can really be expressed as functions on types which is more natural than using MTPC-constraints. Infox operands are particularly expressive. For example: -- Add type family type family (:+:) x y :: * - According to http://www.haskell.org/pipermail/haskell/2007-September/019759.html They fit in a nicer way with the Haskell standard. * Negative part: - They don't allow defining relational type operations, which is _really_ important to dramatically reduce the number of necessary instances. i.e Pred cannot be defined out of Succ, this declaration is ilegal type family Pred x :: * type instance Pred (Succ x) = x whereas it is perfecty possible using MTPCs class (Pos x, Nat y) => Pred x y | x -> y, y -> x instance Succ x y => Pred y x - I'm maybe missing something, but it seems that type synonym families don't work nicely with typeclass constraints. this is ilegal (syntax error): type family Nat x => Succ x :: * this is ilegal too (another syntax error): type instance Nat x => Succ (x :* D0) = x :* D1 and finally I was really surprised to know that, using the type synonym family this is illegal too! toInt (succRef d0) the expression above leads to the following error: No instance for (Data.TypeLevel.Num.Sets.NatI (Succ D0)) But .... Succ D0 = D1, and D1 and D1 _is_ an instance of NatI ! ---- Conclusion ---- So, it seems that with or without type synonym families GADTs don't work so nicely to emulate dependent types (or at least numer-parameterized data structures), so I'll move on to traditional phantom types unless someone provides a solution. I'd like to be proven wrong (we'll lose pattern matching without GADTs which is a pity) but I cannot come up with a better solution. On the other hand, using phantom type parameters allows to encapsulate whichever vector representation we want "under the hood". And that's actually the approach followed by Fredrik Eaton's Vectro library: http://ofb.net/~frederik/vectro/ So unless someone provides a solution for the GADT problem, I think the best option would be simply adapting Vectro to work with the type-level library.
The result would be a function with the following type:
vector :: [a] -> (forall s . Nat s => FSVec s a -> w) -> w
Nevertheless, I'm not fully satisfied by it.
I suppose, it's the best we can get without having dependent types. Others might correct me.
Well, if that's all it can be done (and that's what it seems from your answers), satisfied or not, there's no better option.
Some remark concerning spelling: Would it be possible to drop the V at the end of the function names? I think the fact that those functions are about "vectors" is better expressed by qualification:
Right, do you think the same should be done for Data.TypeLeve.Num.Ops ? (succRef, predRef, addRef, subRef and friends ...) Best Regards, A really frustrated Fons PS: Implemnetaion of Succ using a type synonym family. -- | Successor type-level relation type family Succ x :: * type instance Succ D0 = D1 type instance Succ D1 = D2 type instance Succ D2 = D3 type instance Succ D3 = D4 type instance Succ D4 = D5 type instance Succ D5 = D6 type instance Succ D6 = D7 type instance Succ D7 = D8 type instance Succ D9 = (D1 :* D0) type instance Succ (x :* D0) = x :* D1 type instance Succ (x :* D1) = x :* D2 type instance Succ (x :* D2) = x :* D3 type instance Succ (x :* D3) = x :* D4 type instance Succ (x :* D4) = x :* D5 type instance Succ (x :* D5) = x :* D6 type instance Succ (x :* D6) = x :* D7 type instance Succ (x :* D7) = x :* D8 type instance Succ (x :* D8) = x :* D9 type instance Succ (x :* D9) = (Succ x) :* D0 -- | value-level reflection function for the succesor type-level relation -- (named succRef to avoid a clash with 'Prelude.succ') succRef :: Nat x => x -> Succ x succRef = undefined

Am Montag, 11. Februar 2008 18:17 schrieben Sie:
[…]
As suggested by the pointer you provided, I redefined FSVec and tailV using a transformating of Succ into a type synonym family (see the end of this mail for its full definition) but it didn't help.
Be careful! Type family support is still broken in GHC 6.8. And the whole type system machinery seemed to be rather broken when I last checked a then current development version (6.9).
data FSVec :: * -> * -> * where NullV :: FSVec D0 a (:>) :: FSVec s a -> FSVec (Succ s) a
tailV :: FSVec s a -> FSVec (Succ s) a tailV (x :> xs) = xs
tailV leads to this error (which I don't really understand :S)
GADT pattern match in non-rigid context for `:>' Tell GHC HQ if you'd like this to unify the context In the pattern: x :> xs In the definition of `tailV': tailV (x :> xs) = xs
I think, this can be solved with a type declaration. At least I heard about something like this. Probably googling for this error message will help.
My first impressions about using type synonym families (as opposed to multiparameter type classes) to fake dependent types are:
[…]
I think, type synonym families are not a replacement for multiparameter classes but for functional dependencies. So you will still need multiparameter classes in certain places but you’ll be able to get rid of functional dependencies (except for certain cases where fundeps are combined with overlapping instances, at least).
- I'm maybe missing something, but it seems that type synonym families don't work nicely with typeclass constraints.
this is ilegal (syntax error):
type family Nat x => Succ x :: *
this is ilegal too (another syntax error):
type instance Nat x => Succ (x :* D0) = x :* D1
Maybe you should put your type family synonym into a class where it becomes an associated type synonym: class Nat x where type Succ x :: * […]
and finally I was really surprised to know that, using the type synonym family this is illegal too!
toInt (succRef d0)
the expression above leads to the following error:
No instance for (Data.TypeLevel.Num.Sets.NatI (Succ D0))
But .... Succ D0 = D1, and D1 and D1 _is_ an instance of NatI !
Maybe a bug. As I said, you cannot expect type families to work reliably at the moment.
[…]
Some remark concerning spelling: Would it be possible to drop the V at the end of the function names? I think the fact that those functions are about "vectors" is better expressed by qualification:
Right, do you think the same should be done for Data.TypeLeve.Num.Ops ? (succRef, predRef, addRef, subRef and friends ...)
Yes, I think so.
Best Regards,
A really frustrated Fons
:-( Best wishes, Wolfgang
[…]

I asked Oleg regarding the use of GADTs to emulate dependent types. My
conclusion is that I should forget about them. Here is the full
answer:
---------- Forwarded message ----------
From:
The main question is whether it is feasible to use GADTs to define fixed-sized vectors or not. The motivation behind it is to avoid needing to code your own trusted core and use the compiler typechecker for that purpose instead.
That is a false proposition. In Haskell, any code that uses GADT *MUST* use a run-time test (run-time pattern match). Otherwise, the code is unsound and provides no guarantees. The particular value of a GADT data type is a witness of a proposition expressed in the type of GADT (e.g., type equality). Since it is possible in Haskell to fake this witness (just write undefined), if you don't check the witness at run-time, that is, test that the witness is a real value rather than undefined, you don't get any guarantees. That is why the irrefutable pattern-match does not work with GADT. Incidentally, the first implementation of GADT in GHC did permit irrefutable pattern-match, which did let one write unsound code (that is, code that gave segmentation fault): http://okmij.org/ftp/Haskell/GADT-problem.hs The issue is not present in Coq, for example, whose core calculus is sound and you cannot fake the evidence. Thus, the unsoundness of Haskell (the presence of undefined) mandates the run-time test for any code that uses GADT. So, GADTs are fundamentally less efficient than the typeclasses; the latter can provide assurances that can be checked at compile-time only, with no run-time artifacts.
data FSVec :: * -> * -> * where NullV :: FSVec D0 a (:>) :: Succ s s' => a -> FSVec s a -> FSVec s' a
That is an old problem, and a difficult problem. In Singapore I was talking to Zhaohui Luo (you can look him up on Google), who said that the problem of asserting two types being equal (that is, Succ D0 being equal to D1) is a very tough one. Conor McBride have written a paper on observational equality -- but this is not Haskell. So, I don't think this approach works. Cheers, Oleg

Brandon S. Allbery KF8NH wrote:
On Feb 8, 2008, at 11:14 , Stefan Monnier wrote:
You seem to write 12 as 1 :+ 2 instead of () :+ 1 :+ 2. But I think, the latter representation should probably be prefered. (...) How 'bout treating :+ as similar to `append' rather than similar to `cons'? Basically treat :+ as taking 2 numbers (rather than a number and a digit).
Dumb questions department: why not define e.g. D'0 .. D'9 as () :* 0 .. () :* 9? Programmers then get D'1 :* 2, but the library sees () :* 1 :* 2.
No, D'0 should be (), not () :* D0. If you allow () :* D0, then you introduce redundant types for the same number: In the first case, D'0 :* D'3 == D'3, and D'0 :* D'0 has no instance. In your example, D'3 and D'0 :* D'3 are equivalent, but no longer unify. Dan

Dan Weston wrote:
Brandon S. Allbery KF8NH wrote:
On Feb 8, 2008, at 11:14 , Stefan Monnier wrote:
You seem to write 12 as 1 :+ 2 instead of () :+ 1 :+ 2. But I think, the latter representation should probably be prefered. (...) How 'bout treating :+ as similar to `append' rather than similar to `cons'? Basically treat :+ as taking 2 numbers (rather than a number and a digit).
Dumb questions department: why not define e.g. D'0 .. D'9 as () :* 0 .. () :* 9? Programmers then get D'1 :* 2, but the library sees () :* 1 :* 2.
No, D'0 should be (), not () :* D0. If you allow () :* D0, then you introduce redundant types for the same number:
In the first case, D'0 :* D'3 == D'3, and D'0 :* D'0 has no instance. In your example, D'3 and D'0 :* D'3 are equivalent, but no longer unify.
Dan
On second thought, how would you write D'3 :* D0 ? I think maybe using the () makes it fundamentally difficult to restrict multiple types for the same number.

Am Freitag, 8. Februar 2008 17:14 schrieb Stefan Monnier:
You seem to write 12 as 1 :+ 2 instead of () :+ 1 :+ 2. But I think, the latter representation should probably be prefered. With it, :+ always has a number as its left argument and a digit as its right. Without the () :+ we get ugly exceptional cases. You can see this, for example, in the instance declarations for Compare. With the second representation, we could reduce the number of instances dramatically. We would define a comparison of digits (verbose) and than a comparison of numbers based on the digit comparison (not verbose).
Even if () would be preferred from the programmers point of view (I'm not sure how much we could reduce the number of instances though), it makes the representation less attractive on the user-side. Anyone using the library would find it annoying and would wonder why is it neccessary.
I wouldn’t wonder. Leaving out the () :* part just works because our type-level “values” are not typed, i.e., there aren’t different kinds Digit and Number but only kind *. If :+ would be a data constructor (on the value level), it would take a number and a digit argument which would forbid using a digit as its left argument. So I consider using a digit on the left as “unclean”. It’s similar to using a number as the second part of a cons cell in LISP.
How 'bout treating :+ as similar to `append' rather than similar to `cons'? Basically treat :+ as taking 2 numbers (rather than a number and a digit).
So what would (D1 :* D1) :* (D2 :* D2) mean then?
Stefan
Best wishes, Wolfgang

On Feb 9, 2008 4:08 PM, Wolfgang Jeltsch
So what would (D1 :* D1) :* (D2 :* D2) mean then?
Nothing. That value doesn't satisfy the Nat or Post class constraints and should be taken into consideration. Why should :* be provided a meaning? it is an unavoidable syntactical connective for all that I care. The meaning is provided by class constraints and that's all that matter from the semantical point of view.

Am Samstag, 9. Februar 2008 23:46 schrieben Sie:
On Feb 9, 2008 4:08 PM, Wolfgang Jeltsch
wrote: So what would (D1 :* D1) :* (D2 :* D2) mean then?
Nothing. That value doesn't satisfy the Nat or Post class constraints and should be taken into consideration.
Why should :* be provided a meaning? it is an unavoidable syntactical connective for all that I care. The meaning is provided by class constraints and that's all that matter from the semantical point of view.
I was just refering to Stefan’s argument that :* should be treated as a form of concatenation. From your point of view, it’s clear, of course. Best wishes, Wolfgang

On Feb 8, 2008 5:14 PM, Stefan Monnier
How 'bout treating :+ as similar to `append' rather than similar to `cons'? Basically treat :+ as taking 2 numbers (rather than a number and a digit).
Interpreting it like that would certainly make make more sense. But again, I think that :* should be interpreted just as an unavoidable syntactical annoyance without meaning or interpretation.

On Feb 8, 2008 4:10 PM, Wolfgang Jeltsch
Am Donnerstag, 7. Februar 2008 16:31 schrieben Sie:
Even if () would be preferred from the programmers point of view (I'm not sure how much we could reduce the number of instances though), it makes the representation less attractive on the user-side. Anyone using the library would find it annoying and would wonder why is it neccessary.
I wouldn't wonder. Leaving out the () :* part just works because our type-level "values" are not typed, i.e., there aren't different kinds Digit and Number but only kind *. If :+ would be a data constructor (on the value level), it would take a number and a digit argument which would forbid using a digit as its left argument.
Well, the fact is that :+ (or :* as it is called now) is not a value constructor but a type constructor as you said, so I don't think your example really applies here. Besides, you should be regarded :* as (,) and not as a constructor which "would take a number and a digit argument which would forbid using a digit as its left argument." Indeed, :* exists as a value-level constructor too and works exactly like that. Furthermore, you probably consider using () as natural and logical because you are still thinking from the implementation side. If you forget the implementation details and think as a user who barely wants to write type-level numerical literals, :* is simply an ugly syntactic requirement which we cannot get rid of (I would be happy to find another representation closer to a literal, but I couldn't until now). That is not the case for (), since, as shown in the initial implementation, can be avoided. So, for me, it's just a matter of usability and syntax, the closer the representation can be to literals, the better. I don't see the semantic implications of :* as a valid argument. For me, :* is just an unavoidable ugly syntactical token without meaning. Imagine that for some reason, adding () as a prefix in every numerical literal made the implementation of a compiler slightly easier/faster. I bet users would rant about it till exhaustion :) If the argument was that, for some reason, () was proven to speed up the implementation or make a big maintainability difference (I still have my doubts) it would maybe make more sense (I still wouldn't be sure if it pays off though). Maybe it would be a good idea to create a patch and see what happens :) As a side note, I think that type-value digits actually are "typed" (metatyped maybe is a nicer term?). Class constraints take the role of types in this case. After all (sorry if the definition is imprecise), a type establishes a set of valid values. "Nat n => n" does exactly that. For example, it forces type-level naturals to be normalized (i.e. numerals with leading zeros don't satisfy the Nat constraint)
So I consider using a digit on the left as "unclean". It's similar to using a number as the second part of a cons cell in LISP.
Again, the comparisson is based on semantical implications of the implementation which shouldn't be visible for, or at least not taken into consideration by, the final user.

On Feb 9, 2008 11:33 PM, Alfonso Acosta
On Feb 8, 2008 4:10 PM, Wolfgang Jeltsch
wrote:
example really applies here. Besides, you should be regarded :* as (,) and not as a constructor which "would take a number and a digit
Sorry for my lousy English, I meant "you should regard" not "you should be regarded".

Am Samstag, 9. Februar 2008 23:33 schrieben Sie:
On Feb 8, 2008 4:10 PM, Wolfgang Jeltsch
wrote: Am Donnerstag, 7. Februar 2008 16:31 schrieben Sie:
Even if () would be preferred from the programmers point of view (I'm not sure how much we could reduce the number of instances though), it makes the representation less attractive on the user-side. Anyone using the library would find it annoying and would wonder why is it neccessary.
I wouldn't wonder. Leaving out the () :* part just works because our type-level "values" are not typed, i.e., there aren't different kinds Digit and Number but only kind *. If :+ would be a data constructor (on the value level), it would take a number and a digit argument which would forbid using a digit as its left argument.
Well, the fact is that :+ (or :* as it is called now) is not a value constructor but a type constructor as you said, so I don't think your example really applies here. Besides, you should be regarded :* as (,) and not as a constructor which "would take a number and a digit argument which would forbid using a digit as its left argument." Indeed, :* exists as a value-level constructor too and works exactly like that.
Furthermore, you probably consider using () as natural and logical because you are still thinking from the implementation side. If you forget the implementation details and think as a user who barely wants to write type-level numerical literals, :* is simply an ugly syntactic requirement which we cannot get rid of (I would be happy to find another representation closer to a literal, but I couldn't until now). That is not the case for (), since, as shown in the initial implementation, can be avoided.
So, for me, it's just a matter of usability and syntax, the closer the representation can be to literals, the better. I don't see the semantic implications of :* as a valid argument. For me, :* is just an unavoidable ugly syntactical token without meaning. Imagine that for some reason, adding () as a prefix in every numerical literal made the implementation of a compiler slightly easier/faster. I bet users would rant about it till exhaustion :)
If the argument was that, for some reason, () was proven to speed up the implementation or make a big maintainability difference (I still have my doubts) it would maybe make more sense (I still wouldn't be sure if it pays off though). Maybe it would be a good idea to create a patch and see what happens :)
As a side note, I think that type-value digits actually are "typed" (metatyped maybe is a nicer term?). Class constraints take the role of types in this case.
After all (sorry if the definition is imprecise), a type establishes a set of valid values. "Nat n => n" does exactly that. For example, it forces type-level naturals to be normalized (i.e. numerals with leading zeros don't satisfy the Nat constraint)
So I consider using a digit on the left as "unclean". It's similar to using a number as the second part of a cons cell in LISP.
Again, the comparisson is based on semantical implications of the implementation which shouldn't be visible for, or at least not taken into consideration by, the final user.
My arguments were not so much about implementation, I think. I’d see a type-level number as a list of digits, and a list has the form x1 : (x2 : (… : (xn : [])…)) or ((…([] `Snoc` x1) `Snoc` …) `Snoc` x(n-1)) `Snoc` xn. From this point of view, it’s reasonable to have the extra () :*. On the other hand, it might be sensible to give the user the illusion that :* is part of a special syntax while it’s actually an operator. I’m not really sure what the better approach is. Best wishes, Wolfgang

cat Go.hs import Data.TypeLevel.Num.Reps
This may be a GHC bug, but even though in the module Data.TypeLevel.Num.Reps has the header {-# LANGUAGE EmptyDataDecls, TypeOperators #-} I still get an error with both ghc and ghci version 6.8.2 unless I throw in the -XTypeOperators flag. main = return (undefined :: D2 :+ D1) >> print "Done"
ghc --make Go.hs [1 of 1] Compiling Main ( Go.hs, Go.o )
Go.hs:3:31: Illegal operator `:+' in type `D2 :+ D1' (Use -XTypeOperators to allow operators in types)
ghc --make -XTypeOperators Go.hs [1 of 1] Compiling Main ( Go.hs, Go.o ) Linking Go ...
ghc --version The Glorious Glasgow Haskell Compilation System, version 6.8.2
Dan Alfonso Acosta wrote:
Don't expect anything astonishing yet, but an initial version of the library can be found at
http:/code.haskell.org/type-level
To make reviewing easier I have put the haddock-generated documentation at
http://code.haskell.org/~fons/type-level/doc/
Some remarks:
* Only Positive and Natural numerals in decimal representation are supported. It would be cool to add support for Integers though.
* The code is based on Oleg's implimentation of type-level binaries http://okmij.org/ftp/Computation/resource-aware-prog/BinaryNumber.hs
* exponentiation/log and GCD is not implemented yet
* multiplication is not relational and thus division is broken. I tried porting Oleg's multiplication algorithm without success.
* Aliases (in binary, octal decimal and hexadecimal form) for type-level values and their value-level reflection functions are generated with TH. That implies: * Long compilation time depending on your system * Although errors will always be reported in decimal form, the end user can input values using other bases (only for values in the range of generated aliases of course)
* It would be cool to have "real" support for other bases apart from decimals * It would imply having unlimited size of input for other bases (right now if we want to input a value out of the alises limit, decimal reprentation is mandatory) * However, is it feasible? How could it be done without needing to implement the operations for each base? WOuld it be possible to "overload" the type-level operators so that they worked with different representations and mixed representation arguments?
* Booleans are not implemented (Wolfgang?)
I would be happy to hear any suggestions, get code reviews and/or contributions.
Cheers,
Fons
On Feb 7, 2008 11:17 AM, Wolfgang Jeltsch
wrote: The other library I use for type-level programming is HList. It has type-level booleans already so you might what to take a look at it if you're not already familiar with it. Thanks I'll have a look at it. I have to admit that I don't like the names HBool, HTrue and HFalse. What do
Am Donnerstag, 7. Februar 2008 02:47 schrieb Alfonso Acosta: they mean? Heterogenous booleans? Heterogenous truth? Why it's "Bool" instead of "Boolean" and therefore not conforming to the Prelude convention?
Heterogenous lists are not directly about type level computation. A HList type is usually inhabited. On the other hand, types which denote type level naturals or type-level booleans are empty data types. So type level booleans should go into some type level programming library like the one Alfonso and I want to create. HList should then use these booleans. This is at least my opinion.
Best wishes, Wolfgang
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Feb 7, 2008 9:01 PM, Dan Weston
This may be a GHC bug, but even though in the module Data.TypeLevel.Num.Reps has the header
{-# LANGUAGE EmptyDataDecls, TypeOperators #-}
I still get an error with both ghc and ghci version 6.8.2 unless I throw in the -XTypeOperators flag.
If you are using type operators in a module you have to supply the flag, independently of what flags are supplied in other modules. The same applies for other extensions which modify the _syntax_ of the language (e.g. Template Haskell etc ...) So, it's not a bug. As a side note, even if the TypeOperators flag is supplied GHC 6.8.2 fires an error with the following declaration: instance (Compare x y CEQ) => x :==: y -- GHC fires a "Malformed instance header" error whereas using an equivalent prefix definition works just fine instance (Compare x y CEQ) => (:==:) x y

On Feb 6, 2008 8:47 PM, Alfonso Acosta
On Feb 7, 2008 2:30 AM, Bjorn Buckwalter
wrote: Ok. Is this what people want -- one big hold-all library with everything, as opposed to smaller more specialized packages? I guess I can see advantages (real or perceived) to both approaches.
Apart from Dockins' typenats library there are no other user-friendly specific type-level libraries that know, so I cannot really tell if people would prefer a hold-all library or a couple of more granular specialized ones.
Right now is not hold-all at all (it is still vaporware actually :)), so I think there's no reason to discuss that at this point. Let's see what people think.
Right, of course. I'll be taking a look at your no-longer-vaporware! ;)

On 2008-02-05, Alfonso Acosta
On Feb 5, 2008 4:10 PM, Henning Thielemann
wrote: On Fri, 1 Feb 2008, Aaron Denney wrote:
On 2008-02-01, Bjorn Buckwalter
wrote: If Naturals had been sufficient for me I wouldn't have done my own implementation (I'm unaware of any other implementation of Integers). And there is certainly a lot of value to the clearer error messages from a decimal representation.
I did a balanced-base-three (digits are 0, and +- 1) representation to get negative "decimals".
Nice. In German the digit values are sometimes called "eins, keins, meins". :-)
I'm almost done with the decimal library but it would be nice to check some Integer implementations for future inclusion. So, Aaron, Björn, are your implementations available somewhere?
http://ofb.net/~wnoise/repos/dimensional/ -- Aaron Denney -><-

Am Freitag, 1. Februar 2008 13:09 schrieben Sie:
What about FixedVector for the vector library and DecTypArith (maybe too long) or DecTypes for the type-level decimal arithmetic library?
Actually it would maybe be better to create common high-level interface that could include unary, binary and decimal arithmetic so that the library could be easily reused in other projects (people like Bjorn, seem to be using the unary implementation). I don't know if it would be feasible though.
I’d say, let’s start with the decimal thing. We can extend our package later if there’s a need to do this, can’t we? Best wishes, Wolfgang

On Feb 1, 2008 10:33 PM, Wolfgang Jeltsch
Actually it would maybe be better to create common high-level interface that could include unary, binary and decimal arithmetic so that the library could be easily reused in other projects (people like Bjorn, seem to be using the unary implementation). I don't know if it would be feasible though.
I'd say, let's start with the decimal thing. We can extend our package later if there's a need to do this, can't we?
OK, let's do it like that.

Am Freitag, 1. Februar 2008 13:00 schrieb Alfonso Acosta:
On Jan 31, 2008 11:35 PM, Wolfgang Jeltsch
wrote: Am Donnerstag, 31. Januar 2008 18:30 schrieb Dominic Steinitz:
Look at
This is essentially what I had in mind. While Oleg's implementation needs a "thrusted core", the GADT solution doesn't.
True. However using GADTs doesn't allow to internally make use of Arrays, which (tell me if I'm wrong) are likely to be faster than the naive GADT implementation.
It depends. My first GADT implementation is equivalent to the [] type and often [] is better than arrays. For example, if you read the contents of a file and process it with maps, filters, etc., [] is likely to give you constant space usage which arrays don’t. If you want to lookup elements by index, then arrays are better, of course. For my purpose, it would be fine to use a []-like implementation, I think.
[…]
Some words on the representation of decimal numbers as types. While the representation with types of the form D1 (D2 (D3 Sz)) has the advantage of allowing numbers of arbitrary size, it has the disadvantage of a growing number of parantheses. In my opinion, it would be nicer to have somethink like D1 :- D2 :- D9 :- () with a right-associative operator :-. We could even build the digit list the other way round—() :- D1 :- D2 :- D9—using a left-associative :-. With the latter representation, we wouldn't need to reverse digit sequences when adding numbers.
Right, I agree. I think we should use the arbitrary-size implementation
So let’s use the representation with the left-associative :- (or whatever operator we might choose).
(actually, how arbitrary is it? what's the limit of GHC, if any?).
Arbitrary enough, I think. If we don’t need lists with billions of elements, our representations will have less than 8 digits.
To make it friendlier for the end user I thought about defining aliases for lets say the first 10000 numbers using Template Haskell. That could even make error reports friendlier (not sure to what point though). What do you think?
I have no clear opinion about that at the moment. Maybe it’s okay to use the representation directly. This way, we don’t introduce a dependeny on the Template Haskell language extension (which is only supported by GHC), and the actual representation will occur in error messages anyway whenever the message shows a computed number.
So, we'll be making two separate libraries then. We should think about names.
What about FixedVector for the vector library and DecTypArith (maybe too long) or DecTypes for the type-level decimal arithmetic library?
Alas, there is an inconsistency in naming packages already. Some prefer names which are entirely lowercase, some prefer camel case. I prefer lowercase, with hyphens separating parts of the name. And I also don’t like unusual abbreviations like “typ” (not much shorter than “type”). To mention arithmetics is not so important. So maybe something like “type-level-decimals”? Maybe it’s better to put different type-level programming things into a single package. Then we could name this package “type-level” or something similar. We could start with our decimals. Other type-level things could be added later. I already have some code about type-level booleans. It’s not very sensible to put these few lines into a separate package. It might be nice if we had a general type-level programming package where I could put this code into. As for the name of the fixed-size list package, I have to say that I don’t like the term “vector” in this context. A vector is actually something with addition and scalar multiplication defined on it. Maybe we should make also this package’s scope wider. What about something like “safe-data” or similar?
I'll put my hands dirty once we agree on this.
Great!!
Cheers,
Fons
Best wishes, Wolfgang

On Feb 1, 2008 10:32 PM, Wolfgang Jeltsch
Am Freitag, 1. Februar 2008 13:00 schrieb Alfonso Acosta:
On Jan 31, 2008 11:35 PM, Wolfgang Jeltsch
This is essentially what I had in mind. While Oleg's implementation needs a "thrusted core", the GADT solution doesn't.
True. However using GADTs doesn't allow to internally make use of Arrays, which (tell me if I'm wrong) are likely to be faster than the naive GADT implementation.
It depends. My first GADT implementation is equivalent to the [] type and often [] is better than arrays. For example, if you read the contents of a file and process it with maps, filters, etc., [] is likely to give you constant space usage which arrays don't. If you want to lookup elements by index, then arrays are better, of course. For my purpose, it would be fine to use a []-like implementation, I think.
For mine it would be fine too. Let's implement our needs and then maybe extend it if someone rants about it.
To make it friendlier for the end user I thought about defining aliases for lets say the first 10000 numbers using Template Haskell. That could even make error reports friendlier (not sure to what point though). What do you think?
I have no clear opinion about that at the moment. Maybe it's okay to use the representation directly. This way, we don't introduce a dependeny on the Template Haskell language extension (which is only supported by GHC), and the actual representation will occur in error messages anyway whenever the message shows a computed number.
Well, my EDSL already makes extensive use of TH. So, being selfish, it wouldn't be a problem for me (or any other GHC user) and I think it would make the library much more usable. Just compare f :: List (() :- D1 :- D0 :- D0 :- 1000) Int -> List (() :- D1 :- D0 :- D0 :- D0) Int with, let's say f :: List A1000 Int -> List A1000 Int Again, if someone complains about the TH dependency, the aliases could be generated by TH but saved statically in a module for each release.
So, we'll be making two separate libraries then. We should think about names.
What about FixedVector for the vector library and DecTypArith (maybe too long) or DecTypes for the type-level decimal arithmetic library?
Alas, there is an inconsistency in naming packages already. Some prefer names which are entirely lowercase, some prefer camel case. I prefer lowercase, with hyphens separating parts of the name. And I also don't like unusual abbreviations like "typ" (not much shorter than "type"). To mention arithmetics is not so important. So maybe something like "type-level-decimals"?
Maybe it's better to put different type-level programming things into a single package. Then we could name this package "type-level" or something similar. We could start with our decimals. Other type-level things could be added later. I already have some code about type-level booleans. It's not very sensible to put these few lines into a separate package. It might be nice if we had a general type-level programming package where I could put this code into.
Sounds sensible. However, I would rather prefer something like type-level-comp (from type level computations) or type-level-prog (from type level programming). Type level by itself doesn't describe the functionality of the package.
As for the name of the fixed-size list package, I have to say that I don't like the term "vector" in this context. A vector is actually something with addition and scalar multiplication defined on it. Maybe we should make also this package's scope wider. What about something like "safe-data" or similar?
I think safe-data is a bit too general and might lead to confusion with other safe data packages (namely Mitchell's Safe library). Since the main particularity of the library is that safety properties are achieved via "emulating" dependent types I think that light-dependent-types (from lightweight dependent types), number-parameterized-data or simply parameterized-data (this is the name I like best) would be more appropiate.

On Feb 2, 2008 2:54 PM, Alfonso Acosta
Just compare
f :: List (() :- D1 :- D0 :- D0 :- 1000) Int -> List (() :- D1 :- D0 :- D0 :- D0) Int
I meant f :: List (() :- D1 :- D0 :- D0 :- D0) Int -> List (() :- D1 :- D0 :- D0 :- D0) Int sorry for the typo

Am Samstag, 2. Februar 2008 14:54 schrieben Sie:
On Feb 1, 2008 10:32 PM, Wolfgang Jeltsch wrote:
Am Freitag, 1. Februar 2008 13:00 schrieb Alfonso Acosta: […]
To make it friendlier for the end user I thought about defining aliases for lets say the first 10000 numbers using Template Haskell. That could even make error reports friendlier (not sure to what point though). What do you think?
I have no clear opinion about that at the moment. Maybe it's okay to use the representation directly. This way, we don't introduce a dependeny on the Template Haskell language extension (which is only supported by GHC), and the actual representation will occur in error messages anyway whenever the message shows a computed number.
Well, my EDSL already makes extensive use of TH. So, being selfish, it wouldn't be a problem for me (or any other GHC user) and I think it would make the library much more usable.
Just compare
f :: List (() :- D1 :- D0 :- D0 :- 1000) Int -> List (() :- D1 :- D0 :- D0 :- D0) Int
with, let's say
f :: List A1000 Int -> List A1000 Int
Again, if someone complains about the TH dependency, the aliases could be generated by TH but saved statically in a module for each release.
Hmm, this could be a compromise although I’m not sure whether it is sensible to have a module with thousands of declarations. Another solution would be to put the Template Haskell convenience stuff into a separate package. The core package would probably be usable with Hugs too, while the convenience package would be usable only with GHC. At the moment, I’m not sure how often I’ll need to state type-level numbers explicitely. So currently I cannot know how important aliases for type-level numbers are.
So, we'll be making two separate libraries then. We should think about names.
What about FixedVector for the vector library and DecTypArith (maybe too long) or DecTypes for the type-level decimal arithmetic library?
Alas, there is an inconsistency in naming packages already. Some prefer names which are entirely lowercase, some prefer camel case. I prefer lowercase, with hyphens separating parts of the name. And I also don't like unusual abbreviations like "typ" (not much shorter than "type"). To mention arithmetics is not so important. So maybe something like "type-level-decimals"?
Maybe it's better to put different type-level programming things into a single package. Then we could name this package "type-level" or something similar. We could start with our decimals. Other type-level things could be added later. I already have some code about type-level booleans. It's not very sensible to put these few lines into a separate package. It might be nice if we had a general type-level programming package where I could put this code into.
Sounds sensible. However, I would rather prefer something like type-level-comp (from type level computations) or type-level-prog (from type level programming). Type level by itself doesn't describe the functionality of the package.
Hmm, package names don’t have to be descriptive. Short names tend to sound better and be easier to remember. My FRP GUI and graphics library is named Grapefruit. This name makes hardly any sense. It refers to the previous library Fruit and the fact that I like Grapefruits—nothing more. But it’s a name, people can remember more easily than FRGGLER (Functional Reactive GUI and Graphics Library with Extensible Records). ;-) In addition, abbreviations like “comp” typically have the problem of being ambiguous: computation, composition, component, … So I still prefer type-level.
As for the name of the fixed-size list package, I have to say that I don't like the term "vector" in this context. A vector is actually something with addition and scalar multiplication defined on it. Maybe we should make also this package's scope wider. What about something like "safe-data" or similar?
I think safe-data is a bit too general and might lead to confusion with other safe data packages (namely Mitchell's Safe library). Since the main particularity of the library is that safety properties are achieved via "emulating" dependent types I think that light-dependent-types (from lightweight dependent types), number-parameterized-data or simply parameterized-data (this is the name I like best) would be more appropiate.
parametrized-data looks good. The others seem to be too long again. So type-level + parametrized-data is my vote. But don’t let’s spend too much time discussing the name. ;-) Best wishes, Wolfgang

On Feb 4, 2008 12:36 PM, Wolfgang Jeltsch
Am Samstag, 2. Februar 2008 14:54 schrieben Sie:
Again, if someone complains about the TH dependency, the aliases could be generated by TH but saved statically in a module for each release.
Hmm, this could be a compromise although I'm not sure whether it is sensible to have a module with thousands of declarations.
As long as the module is automatically generated I don't see why it would be a problem. Bear in mind that using TH would, in practice, be equivalent to code such a module by hand anyway.
Another solution would be to put the Template Haskell convenience stuff into a separate package. The core package would probably be usable with Hugs too, while the convenience package would be usable only with GHC.
I'm not sure if it worths it to create a separate package and add another dependency for those who would like to use it. I don't still know how many people would be interested in using the type-level library so, again, I think it won't hurt to include the TH-generated aliases and then change it if some non-GHC user rants about it.
So type-level + parametrized-data is my vote. But don't let's spend too much time discussing the name. ;-)
Fair enough. type-level + parameterized-data it is then (unless someone else has a better suggestion). I'm going to begin coding now. I'll host the project in community.haskell.org, do you have an account there? PS: BTW, I asked Oleg for permission and, as expected, agreed to create the library under a BS-D license.

Am Montag, 4. Februar 2008 13:22 schrieben Sie:
On Feb 4, 2008 12:36 PM, Wolfgang Jeltsch wrote: […]
I don't still know how many people would be interested in using the type-level library so, again, I think it won't hurt to include the TH-generated aliases and then change it if some non-GHC user rants about it.
Okay, let’s do so for now.
So type-level + parametrized-data is my vote. But don't let's spend too much time discussing the name. ;-)
Fair enough. type-level + parameterized-data it is then (unless someone else has a better suggestion). I'm going to begin coding now.
Yes, go ahead. :-) Thanks a lot.
I'll host the project in community.haskell.org, do you have an account there?
Now, I haven’t. :-(
PS: BTW, I asked Oleg for permission and, as expected, agreed to create the library under a BS-D license.
Great. So the packages you create now will be released under BSD3, right? Best wishes, Wolfgang

On Feb 4, 2008 8:27 PM, Wolfgang Jeltsch
Am Montag, 4. Februar 2008 13:22 schrieben Sie:
I don't still know how many people would be interested in using the type-level library so, again, I think it won't hurt to include the TH-generated aliases and then change it if some non-GHC user rants about it.
Okay, let's do so for now.
Actually, I was considering to conditionally include the TH code or not depending on the compiler (using Cabal configurations). I thought "that should make everyone happy". Then, I realized we agreed to make use of infix type constructors anyway (which seems to be a GHC-only extension, tell me if I'm wrong), so the TH dependency is not that important anymore (unless we decide to avoid infix type constructors)
I'll host the project in community.haskell.org, do you have an account there?
Now, I haven't. :-(
Well, you can request one at http://community.haskell.org/admin/account_request.html if you want Otherwise I'll take the maintainer role.
PS: BTW, I asked Oleg for permission and, as expected, agreed to create the library under a BS-D license.
Great. So the packages you create now will be released under BSD3, right?
Yes, that's the intention.

Am Montag, 4. Februar 2008 20:44 schrieben Sie:
I'll host the project in community.haskell.org, do you have an account there?
Now, I haven't. :-(
Well, you can request one at http://community.haskell.org/admin/account_request.html if you want
Otherwise I'll take the maintainer role.
I’m fine with you having the maintainer role as long as you accept a patch from me from time to time. :-) Best wishes, Wolfgang

Alfonso Acosta wrote:
So type-level + parametrized-data is my vote. But don't let's spend too much time discussing the name. ;-)
Fair enough. type-level + parameterized-data it is then (unless someone else has a better suggestion). I'm going to begin coding now.
hang on, "parametrized" or "parameterized"? -- both seem like plausible spellings, but there's an "e" different between what you two said!

Am Montag, 11. Februar 2008 21:44 schrieben Sie:
Alfonso Acosta wrote:
So type-level + parametrized-data is my vote. But don't let's spend too much time discussing the name. ;-)
Fair enough. type-level + parameterized-data it is then (unless someone else has a better suggestion). I'm going to begin coding now.
hang on, "parametrized" or "parameterized"? -- both seem like plausible spellings, but there's an "e" different between what you two said!
What spelling is more common? Best wishes, Wolfgang

Concerning "parametrized" vs "parameterized" On 12 Feb 2008, at 11:21 pm, Wolfgang Jeltsch asked:
What spelling is more common?
Strictly speaking, there is no such word in English as "parametrized". There is no general morphological rule "Xer => Xrized" in English. The only spelling accepted by the OED is "parameterized", which annoys me because I prefer the -ise- spelling. Amusingly, one of the quotations in the www.oed.com entry for "parameterized" actually spells it "parametrized"; I don't know whether the source had it that way or whether the data entry error was at the OED.

Richard A. O'Keefe wrote:
Concerning "parametrized" vs "parameterized" On 12 Feb 2008, at 11:21 pm, Wolfgang Jeltsch asked:
What spelling is more common?
Strictly speaking, there is no such word in English as "parametrized".
Except that, strictly speaking, there *is* a word "parametrized" in English. I have read and heard it from many (at least 10) independent uses by educated native writers of English. I use it myself. Merriam-Webster agrees with me [1]. The OED editorial board is no Académie Anglaise: its criterion is descriptive, not prescriptive, by its own admission [2]. Whether a spelling is accepted by the OED has nothing to do with morphological rules (or "proper" usage) and everything to do with established usage. That is why we say "impulse" but not "compulse", the antonym of "increment" is not "excrement", we have "electrify/liquify" but "electrification/liquefaction", "duh" and "no duh" are near-synonyms, etc. To directly answer Wolfgang's question: "parameterized" is the more common. It is "more correct" only insofar as it is the more common. [1] http://www.merriam-webster.com/dictionary/parametrized Main Entry: pa·ram·e·ter·ize Pronunciation: \p?-?ra-m?-t?-?ri-z, -m?-?tri-z\ Variant(s): or pa·ram·e·trize \-?ra-m?-?tri-z\ Function: transitive verb Inflected Form(s): pa·ram·e·ter·ized or pa·ram·e·trized; pa·ram·e·ter·iz·ing or pa·ram·e·triz·ing Date: 1940 : to express in terms of parameters [2] http://www.oed.com/about/writing/choosing.html "...a new word is not included in the OED unless it has "caught on" and become established in the language. Words that are only used for a short period of time, or by a very small number of people, are not included." Dan Richard A. O'Keefe wrote:
Concerning "parametrized" vs "parameterized" On 12 Feb 2008, at 11:21 pm, Wolfgang Jeltsch asked:
What spelling is more common?
Strictly speaking, there is no such word in English as "parametrized". There is no general morphological rule "Xer => Xrized" in English. The only spelling accepted by the OED is "parameterized", which annoys me because I prefer the -ise- spelling.
Amusingly, one of the quotations in the www.oed.com entry for "parameterized" actually spells it "parametrized"; I don't know whether the source had it that way or whether the data entry error was at the OED.

Am Donnerstag, 14. Februar 2008 03:23 schrieben Sie:
To directly answer Wolfgang's question: "parameterized" is the more common. It is "more correct" only insofar as it is the more common.
So we should “parameterized” for the package name. Best wishes, Wolfgang

On Feb 14, 2008 10:40 AM, Wolfgang Jeltsch
So we should "parameterized" for the package name.
That's the packagename I've been using. I'm done with a basic implementation but I'd like to test some other things before showing the code. On the other hand, I think that the type-level library is almost ready for it's initial release. Before uploading it to Hackage and making an announcement I would be pleased to receive some comments/suggestions. Here is the darcs repository: http://code.haskell.org/type-level/ Here is the haddock-generated documentation: http://code.haskell.org/~fons/type-level/doc/ There are still some things missing which would be cool to have: * Native support of representations in different bases (I don't know if it's feasible) * Support of negative integers (Björn?) * Support of type-level Booleans (Wolfgang?)

Am Dienstag, 19. Februar 2008 21:44 schrieben Sie:
* Support of type-level Booleans (Wolfgang?)
Attached is just a quickly hacked Boolean module. Nothing very special. I’d be happy if you could prettify this (choose better names, add documentation, etc.). Thanks for any effort. Best wishes, Wolfgang

2008/2/19 Wolfgang Jeltsch
Attached is just a quickly hacked Boolean module. Nothing very special. I'd be happy if you could prettify this (choose better names, add documentation, etc.). Thanks for any effort.
Thanks to you for the module. I have a few questions though. Why are the value-level reflecting functionsimplemented as type-class methods? It makes the code more verbose and I don't see any advantage compared to simply defining a function per class. Let me show you an example: This is your implementation of Not: class (Boolean boolean, Boolean boolean') => Not boolean boolean' | boolean -> boolean', boolean' -> boolean where not :: boolean -> boolean' instance Not False True where not _ = true instance Not True False where not _ = false This is how I would do it: class (Boolean boolean, Boolean boolean') => Not boolean boolean' | boolean -> boolean', boolean' -> boolean where instance Not False True instance Not True False not :: Not a b => a -> b not = undefined Furthermore, why did you choose to use Boolean instead of simply Bool? Cheers, Fons

Am Mittwoch, 20. Februar 2008 00:39 schrieben Sie:
Why are the value-level reflecting functionsimplemented as type-class methods? It makes the code more verbose and I don't see any advantage compared to simply defining a function per class. Let me show you an example:
This is your implementation of Not:
class (Boolean boolean, Boolean boolean') => Not boolean boolean' | boolean -> boolean', boolean' -> boolean where not :: boolean -> boolean'
instance Not False True where not _ = true
instance Not True False where not _ = false
This is how I would do it:
class (Boolean boolean, Boolean boolean') => Not boolean boolean' | boolean -> boolean', boolean' -> boolean where
instance Not False True instance Not True False
not :: Not a b => a -> b not = undefined
Your definition of the not function uses the implementation detail that false and true are actually undefined. My implementation of the not function also works if false and true are defined to be something different. Of course, false and true are in the same library, so we know this implementation detail and could make use of it.
Furthermore, why did you choose to use Boolean instead of simply Bool?
To avoid a name clash. Feel free to change this. Note that False and True don’t cause a name clash since they live in a namespace different from the one Prelude’s False and True live in.
Cheers,
Fons
Best wishes, Wolfgang

OK I'll include the module after I change the things mentioned.
BTW, I finally have an initial version of the parameterized-data package:
Darcs repository:
http://code.haskell.org/parameterized-data
Haddock documentation:
http://code.haskell.org/~fons/parameterized-data/doc/
Any comments/suggestions would be appreciated.
To fix the problem of the "vector" constructor I included a Template
Haskell variant. It is quite simple to use:
$ ghci -XTemplateHaskell
Prelude> :m +Data.Param
Prelude Data.Param> $(vectorTH [1 :: Int, 2, 3, 4])
Prelude Data.Param> :t $(vectorTH [1 :: Int, 2, 3, 4])
(vectorTH [1 :: Int, 2, 3, 4]) :: (Num t) => FSVec Data.TypeLevel.Num.Reps.D4 t
Note that the size parameter (type-level decimal numeral) is correct.
It would be nice to be able to use a Quasiquoter [1] (available in GHC
HEAD) to enable pattern matching and custom syntax to Vector literals.
However, I was bitten by a polymorphism problem when implementing it:
see [2] for details
[1] http://www.haskell.org/ghc/dist/current/docs/users_guide/template-haskell.ht...
[2] http://www.haskell.org/pipermail/template-haskell/2008-February/000655.html
On Wed, Feb 20, 2008 at 2:26 AM, Wolfgang Jeltsch
Am Mittwoch, 20. Februar 2008 00:39 schrieben Sie:
Why are the value-level reflecting functionsimplemented as type-class methods? It makes the code more verbose and I don't see any advantage compared to simply defining a function per class. Let me show you an example:
This is your implementation of Not:
class (Boolean boolean, Boolean boolean') => Not boolean boolean' | boolean -> boolean', boolean' -> boolean where not :: boolean -> boolean'
instance Not False True where not _ = true
instance Not True False where not _ = false
This is how I would do it:
class (Boolean boolean, Boolean boolean') => Not boolean boolean' | boolean -> boolean', boolean' -> boolean where
instance Not False True instance Not True False
not :: Not a b => a -> b not = undefined
Your definition of the not function uses the implementation detail that false and true are actually undefined. My implementation of the not function also works if false and true are defined to be something different. Of course, false and true are in the same library, so we know this implementation detail and could make use of it.
Furthermore, why did you choose to use Boolean instead of simply Bool?
To avoid a name clash. Feel free to change this.
Note that False and True don't cause a name clash since they live in a namespace different from the one Prelude's False and True live in.
Cheers,
Fons
Best wishes, Wolfgang _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Am Mittwoch, 20. Februar 2008 09:20 schrieben Sie:
OK I'll include the module after I change the things mentioned.
BTW, I finally have an initial version of the parameterized-data package:
Darcs repository:
http://code.haskell.org/parameterized-data
Haddock documentation:
http://code.haskell.org/~fons/parameterized-data/doc/
Any comments/suggestions would be appreciated.
Hello Fons, why do you use the term vector? I’d say that this term is more or less wrong for what this type is about. The distinguishing property of vectors compared to lists is that there is addition and scalar multiplication for vectors. Being a list of fixed size is not so important for vectors, in fact, it’s completely unnecessary. Real numbers are vectors, functions from real numbers to real numbers are vectors, matrixes are vectors—you just have to provide them with proper addition and scalar multiplication. The data type you defined is a fixed size list.
[…]
Best wishes, Wolfgang

On Wed, Feb 20, 2008 at 11:26 AM, Wolfgang Jeltsch
Hello Fons,
why do you use the term vector? I'd say that this term is more or less wrong for what this type is about. The distinguishing property of vectors compared to lists is that there is addition and scalar multiplication for vectors.
That depends on how you interpret the word vector, which is polysemous: http://en.wikipedia.org/wiki/Vector You are interpreting it as "An element in a vector space, often represented as a coordinate vector" whereas in this case I try to mean "A one-dimensional array".
The data type you defined is a fixed size list.
The fact that FSVec is internally implemented using lists doesn't imply that FSVec should be interpreted as a list. FSVec is an ADT and I could as well have used something else to implement it (inmutable arrays for instance).

Am Samstag, 2. Februar 2008 14:54 schrieben Sie:
On Feb 1, 2008 10:32 PM, Wolfgang Jeltsch wrote:
Am Freitag, 1. Februar 2008 13:00 schrieb Alfonso Acosta:
[…]
To make it friendlier for the end user I thought about defining aliases for lets say the first 10000 numbers using Template Haskell. That could even make error reports friendlier (not sure to what point though). What do you think?
I have no clear opinion about that at the moment. Maybe it's okay to use the representation directly. This way, we don't introduce a dependeny on the Template Haskell language extension (which is only supported by GHC), and the actual representation will occur in error messages anyway whenever the message shows a computed number.
Well, my EDSL already makes extensive use of TH. So, being selfish, it wouldn't be a problem for me (or any other GHC user) and I think it would make the library much more usable.
Hello again, I have a feedback from my Grapefruit co-developer about those aliases in the type-level package. He told me that on his machine, building this package took about 15 minutes, obviously because the machine ran out of RAM. He also told me that the generated object code was very large, and that loading the documentation page generated by Haddock took very long. And he made a very good point: Who needs aliases for *all* numbers until, say, 10000? Who needs to hard-code the vector length 8247 in his code? I think that in practice, you only need very few numbers hard-coded. So it might be better to export a function for letting the package user generate the necessary aliases himself. I even think that it is probably sensible to drop the alias thing completely, at least if you have vector lengths in mind. What vector lengths will appear as fixed in a real world program? 1000? I think, usually you have only sizes like 2 or 3 hard-coded, for vectors in the mathematical sense. And their representation is already very short: D2, D3, … Remember that computed numbers are not shown as aliases in error messages, only numbers that directly appear in your code. My co-author also mentioned that he doesn’t see much use for octal and hexadecimal notation. So I propose to drop alias support from type-level or at least let the package user do the necessary generation.
[…]
Best wishes, Wolfgang

On Fri, Mar 14, 2008 at 5:30 PM, Wolfgang Jeltsch
I have a feedback from my Grapefruit co-developer about those aliases in the type-level package. He told me that on his machine, building this package took about 15 minutes, obviously because the machine ran out of RAM. He also told me that the generated object code was very large, and that loading the documentation page generated by Haddock took very long.
Fair point, it akes quite some time in my machine too (not 15minutes though)
And he made a very good point: Who needs aliases for *all* numbers until, say, 10000? Who needs to hard-code the vector length 8247 in his code?
If I remember correctly aliases are generated until decimal 5000, which might me really long anyway.
I think that in practice, you only need very few numbers hard-coded. So it might be better to export a function for letting the package user generate the necessary aliases himself.
I even think that it is probably sensible to drop the alias thing completely, at least if you have vector lengths in mind. What vector lengths will appear as fixed in a real world program? 1000? I think, usually you have only sizes like 2 or 3 hard-coded, for vectors in the mathematical sense. And their representation is already very short: D2, D3, … Remember that computed numbers are not shown as aliases in error messages, only numbers that directly appear in your code.
My co-author also mentioned that he doesn't see much use for octal and hexadecimal notation.
So I propose to drop alias support from type-level or at least let the package user do the necessary generation.
I think that removing aliases completely is not a good idea. How about generating much lower aliases for decimals (lets say until 1000), droping the other bases, and exporting a function to extended the alias range at will? (that function could perfectly include the other bases as well). Something called extendAliases would do. I don't have the time to work on it now, but if you send me a patch I'd be happy to apply it. It should be something simple to do. All the Template Haskell machinery is already coded.
[…]
Best wishes, Wolfgang

Am Freitag, 14. März 2008 17:46 schrieben Sie:
[…]
I think that removing aliases completely is not a good idea. How about generating much lower aliases for decimals (lets say until 1000),
I don’t think, this is a good idea. Like nobody will need an alias for 8247, nobody will need an alias for 824. The main point is that it is unnecessary to have a continuous range of numbers for which aliases exist. If you need aliases, you need them for “outstanding” values. Maybe you need aliases for all powers of 2 up to a certain number. Or aliases for all square numbers. Therefore I think that if we want aliases then we should let the user and only the user generate them. This has also the interesting consequence that the type-level package doesn’t need the Template Haskell language extension anymore. After all, using the template-haskell package doesn’t imply that you have to have a TH-enabled compiler, as far as I know.
droping the other bases,
That’s a good idea.
and exporting a function to extended the alias range at will?
I’d rather propose something like this: $(numAliasDecls [2 ^ n | n <- 0..16]) So that numAliasDecls has a type like [Int] -> [Decl].
(that function could perfectly include the other bases as well).
Maybe.
[…]
I don't have the time to work on it now, but if you send me a patch I'd be happy to apply it. It should be something simple to do. All the Template Haskell machinery is already coded.
Okay, I could send you a patch realizing my above ideas but would like to hear your opinion first.
[…]
Best wishes, Wolfgang

On Fri, Mar 14, 2008 at 9:58 PM, Wolfgang Jeltsch
Am Freitag, 14. März 2008 17:46 schrieben Sie:
I think that removing aliases completely is not a good idea. How about generating much lower aliases for decimals (lets say until 1000),
I don't think, this is a good idea. Like nobody will need an alias for 8247, nobody will need an alias for 824. The main point is that it is unnecessary to have a continuous range of numbers for which aliases exist. If you need aliases, you need them for "outstanding" values. Maybe you need aliases for all powers of 2 up to a certain number. Or aliases for all square numbers.
Therefore I think that if we want aliases then we should let the user and only the user generate them. This has also the interesting consequence that the type-level package doesn't need the Template Haskell language extension anymore. After all, using the template-haskell package doesn't imply that you have to have a TH-enabled compiler, as far as I know.
I don't agree. Not using aliases is a big pain, which means that every user wanting to use vectors with sizes biger than 10 (i.e. all) will have to generate their own alias set. It would be really annoying having to generate the aliases every time the library is used. Let's agree on a minimum alias set likely to be used by most of the users, small enough to get a reasonable object code size and compile time (we can even prevent haddock from parsing the alias module and just write a comment indicating what aliases are and which ones are generated if that helps). Generating type redefinitions up to decimal 500 shouldn't be such a big overhead (at least compared to the quntity of aliases generated right now).
droping the other bases,
That's a good idea.
and exporting a function to extended the alias range at will?
I'd rather propose something like this:
$(numAliasDecls [2 ^ n | n <- 0..16])
So that numAliasDecls has a type like [Int] -> [Decl].
That doesn't include bases, which might be important for the end user (for example Oleg wrote his type-evel numerical library in base 2 because it was aimed at system development). I'd rather propose a modification of Data.TypeLevel.Num.Aliases.genAliases which probably should be aware of the aliases already generated in the library so that they are not duplicated.

Alfonso Acosta
2) An implementation of type-level Naturals would really help. What has already been done?
I see you have received many suggestions already and personally I don't think you can go wrong with Oleg (et al)'s implementations. For a decimal representation you probably don't have much choice anyway! For completeness let me mention that an unadvertised part of the Dimensional[1] library is an implementation of type-level unary Integers[2] (as opposed to Naturals). Addition, subtraction, multiplication and division is supported. As I side note I've spent quite some time experimenting with fixed-size vectors and matrices with heterogeneous elements. For this I've been using Oleg, Ralf and Keean's HList[3] library which also has type-level unary Naturals. -Bjorn [1] http://dimensional.googlecode.com [2] http://www.buckwalter.se/~bjorn/darcs/dimensional/Numeric/NumType.lhs [3] http://homepages.cwi.nl/~ralf/HList/
participants (13)
-
Aaron Denney
-
Alfonso Acosta
-
Bjorn Buckwalter
-
Brandon S. Allbery KF8NH
-
Bulat Ziganshin
-
Dan Licata
-
Dan Weston
-
Dominic Steinitz
-
Henning Thielemann
-
Isaac Dupree
-
Richard A. O'Keefe
-
Stefan Monnier
-
Wolfgang Jeltsch