Thread: [pure-lang-users] case
Status: Beta
Brought to you by:
agraef
From: Eddie R. <er...@bm...> - 2008-08-27 17:00:28
|
The following works fine except that I don't have a verify function yet to verify that all of the rows are the same length. Probably need to just find the longest row and then set undefined matrix cells to 0? But for the real question. matrix ys@(x:xs) = Matrix type (do (\i -> do (\j -> set m i j (type ((ys!i)!j))) (0..c-1)) (0..r-1) $$ m) when r = #ys; c = #x; type = if foldl1 (*) (map (\x -> all intp x) ys) then int else double; (m, setter) = if type === int then gsl_matrix_int_alloc r c, gsl_matrix_int_set else gsl_matrix_alloc r c, gsl_matrix_set; end; It would be nice if "(m, setter) = .." could be written case type of int = gsl_matrix_int_alloc r c, gsl_matrix_int_set; double = gsl_matrix_alloc r c, gsl_matrix_set; end; but it seems that case uses (==) instead of (===). e.r. |
From: Albert G. <Dr....@t-...> - 2008-08-28 00:36:11
|
Eddie Rucker wrote: > (do (\i -> do (\j -> set m i j (type ((ys!i)!j))) (0..c-1)) (0..r-1) That kind of abomination will hopefully soon become much easier. With the help of macros it should be easy to write an optimization rule for the void combinator so that void [set m i j; i = ...; j = ...] will just translate to a nested do automagically. BTW, using 'set' for the setter is a bad idea, that's already used in set.pure. > case type of > int = gsl_matrix_int_alloc r c, gsl_matrix_int_set; > double = gsl_matrix_alloc r c, gsl_matrix_set; > end; > > but it seems that case uses (==) instead of (===). No it doesn't. Sorry, it's too late again, so I won't rehash the manual here. The explanations of 'head = function' and 'nullary' symbols in the manual should help. (You can't match against an ordinary function symbol, it must be a constant. So you might want to define your own symbolic constants for the different types. Or you could just pass a "witness" of the type and then match against _::int etc.) Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: Eddie R. <er...@bm...> - 2008-08-28 12:51:42
|
On Thu, 2008-08-28 at 02:46 +0200, Albert Graef wrote: > Albert Graef wrote: > >> case type of > >> int = gsl_matrix_int_alloc r c, gsl_matrix_int_set; > >> double = gsl_matrix_alloc r c, gsl_matrix_set; > >> end; > >> > BTW, the above won't work in Haskell or ML either... That poor little > int function doesn't like it if you toss it around as if it was some > piece of silly data! ;-) I thought that was one of the points of using first class functions (ducking tomatoes) ;-) e.r. |
From: John C. <co...@cc...> - 2008-08-28 22:16:27
|
Albert Graef scripsit: > The problem is how to decide which symbols on the lhs of that equation > are literal (function or constant) symbols, and which are the variables > which are to be bound by the equation. How about some syntax, loosely analogous to Common Lisp's #' that indicates that the following symbol is to be interpreted as a function in this particular use? This would make the symbol locally nullary. -- What asininity could I have uttered John Cowan <co...@cc...> that they applaud me thus? http://www.ccil.org/~cowan --Phocion, Greek orator |
From: Albert G. <Dr....@t-...> - 2008-08-28 23:37:27
|
John Cowan wrote: > How about some syntax, loosely analogous to Common Lisp's #' that > indicates that the following symbol is to be interpreted as a function > in this particular use? This would make the symbol locally nullary. That would be a possibility, but I don't like that syntax, and anyway we don't have any special symbols to spare since they might all be defined as operators by the user. Also, frankly I don't think it's an issue since that usage is not all that common and you can just use a guard like f===int instead. Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: Albert G. <Dr....@t-...> - 2008-08-28 00:45:17
|
Albert Graef wrote: >> case type of >> int = gsl_matrix_int_alloc r c, gsl_matrix_int_set; >> double = gsl_matrix_alloc r c, gsl_matrix_set; >> end; >> >> but it seems that case uses (==) instead of (===). BTW, the above won't work in Haskell or ML either... That poor little int function doesn't like it if you toss it around as if it was some piece of silly data! ;-) You could also just do a if type===int then ... else if type==double ..., of course. G'night, Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: Eddie R. <er...@bm...> - 2008-08-28 12:38:38
|
On Thu, 2008-08-28 at 02:37 +0200, Albert Graef wrote: > BTW, using 'set' for the setter is a bad idea, that's already used in > set.pure. Actually the thingy I sent was bad. (m, setter) should have been (m, set) like in the following. Since set is local to matrix and we have lexical scoping, I see no problem. I can see wherethe typo in the previous would lead you to believe set was global. matrix ys@(x:xs) = Matrix type (do (\i -> do (\j -> set m i j (type ((ys!i)!j))) (0..c-1)) (0..r-1) $$ m) when r = #ys; c = #x; type = if foldl1 (*) (map (\x -> all intp x) ys) then int else double; (m, set) = if type === int then gsl_matrix_int_alloc r c, gsl_matrix_int_set else gsl_matrix_alloc r c, gsl_matrix_set; end; > symbolic constants for the different types. Or you could just pass a > "witness" of the type and then match against _::int etc.) I'm thinking about this. The case type of would make for easier reading when I add the complex matrix type in. Hope you got some good sleep. e.r. |
From: Albert G. <Dr....@t-...> - 2008-08-28 15:50:26
|
Eddie Rucker wrote: > Actually the thingy I sent was bad. (m, setter) should have been (m, > set) like in the following. Since set is local to matrix and we have > lexical scoping, I see no problem. Yes, I missed that. But you could still simplify your definition, by just making gsl_matrix_set promote integer arguments to double. I don't see why you need that type argument to the setter function at all. Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: Eddie R. <er...@bm...> - 2008-08-28 17:29:52
|
On Thu, 2008-08-28 at 17:51 +0200, Albert Graef wrote: > Eddie Rucker wrote: > > Actually the thingy I sent was bad. (m, setter) should have been (m, > > set) like in the following. Since set is local to matrix and we have > > lexical scoping, I see no problem. > > Yes, I missed that. But you could still simplify your definition, by > just making gsl_matrix_set promote integer arguments to double. I don't > see why you need that type argument to the setter function at all. > > Albert > Just have double and complex entries? e.r. |
From: Albert G. <Dr....@t-...> - 2008-08-28 17:13:23
|
Eddie Rucker wrote: > On Thu, 2008-08-28 at 02:46 +0200, Albert Graef wrote: >> Albert Graef wrote: >>>> case type of >>>> int = gsl_matrix_int_alloc r c, gsl_matrix_int_set; >>>> double = gsl_matrix_alloc r c, gsl_matrix_set; >>>> end; > >> BTW, the above won't work in Haskell or ML either... That poor little >> int function doesn't like it if you toss it around as if it was some >> piece of silly data! ;-) > > I thought that was one of the points of using first class functions > (ducking tomatoes) ;-) I knew that this was coming. ;-) But of course you're right, functions are as good data as any other and in fact you can compare them with (===). The reason why you can't match against an ordinary (non-nullary) function symbol lies in the "head = function" rule. The "head = function" rule allowed me to get rid of the lexical distinction between function and variable symbols (which is one of Q's worst parts). This applies to all kinds of rewriting rules, not only 'case'. So, consider a rule like: foo bar (baz x) = x+1; The problem is how to decide which symbols on the lhs of that equation are literal (function or constant) symbols, and which are the variables which are to be bound by the equation. Note that Haskell employs the constructor discipline, and even lexically distinguishes constructor symbols, to figure out what the variables are. But for Pure there's no lexical distinction and we don't have (nor want) the constructor discipline either. But if you take another look at this equation, foo bar (baz x) = x+1; it's plain to see for a human reader that foo and baz are function symbols and that bar and x must either be variables or constant symbols since they're leaves of the lhs expression tree. There's no way to decide the latter question, however, without someone telling you, say, that bar is a constant, and x isn't, and that is what Pure's 'nullary' declarations are for. The "head = function" rule just makes precise that intuitive interpretation, so that the compiler can use it to parse the lhs of equations in an unabiguous way. It says that: - The head symbol of a function application, like foo in 'foo bar (baz x)' or baz in 'baz x', is always a literal (function) symbol. - All other symbols (bar and x in this case) are variables, *unless* they're explicitly declared as 'nullary' in which case they're literal (constant) symbols. That rule is just a convention, of course, but it seems perfectly reasonable and in fact it lines up nicely with term rewriting theory. Indeed, all those fixity and nullary declarations in the prelude and in your own programs are just what term rewriting theorists call the "signature" of an algebra, which is simply an "alphabet" of function symbols with their "arities" (number of arguments they expect), with function symbols of arity zero denoting the "constants" of that algebra. The only differences to standard TRS theory are that (1) in Pure you don't have to declare the arities of non-operator and non-nullary symbols, they're inferred automatically; (2) a symbol occuring as a leave on the lhs can denote a variable even if it's also used as a (non-nullary) function symbol elsewhere. Feature #2 is what actually bit you in your 'case' example, so you might argue that this is *not* a good idea. But if you insist that function symbols must never be used as variable names, then a rule like 'foo bla = bla+1' would suddenly change meaning if you happen to add a definition like 'bla x = x-1' somewhere else in your program! That would be very bad indeed and would give rise to horrible bugs almost impossible to avoid in bigger programs. Therefore, (2) is much preferable. In fact, Haskell treats this in exactly the same way. You can even write stuff like 'suck suck = suck+1' which will give you 'suck 99 => 100', in both Haskell and Pure. Try it! (NB: It might be tempting to just declare all your function or at least the constructor symbols 'nullary' to circumvent (2) above. Don't do that, it's a very bad idea for the reasons I just discussed. Nullary symbols should really be reserved for special kinds of atomic values, like [] and () in the prelude. If you really need to introduce nullary symbols, make them stand out or declare them as private symbols, at least if your code is supposed to be used as a library by others.) Back to your example: case type of int = gsl_matrix_int_alloc r c, gsl_matrix_int_set; double = gsl_matrix_alloc r c, gsl_matrix_set; end; It should be clear by now that the rule 'int = gsl_matrix_int_alloc ...' doesn't match against 'int' as a literal symbol, because 'int' is not nullary, so, by what I said above, 'int' is just a variable there! Ok, I hope that this finally clears up the mysteries surrounding Pure's symbol declarations and the "head = function" rule. :) Maybe someone could put this on the wiki, so that we finally have some content for the FAQ section there? HTH, Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: Eddie R. <er...@bm...> - 2008-08-28 17:29:34
|
On Thu, 2008-08-28 at 19:14 +0200, Albert Graef wrote: > Eddie Rucker wrote: > > > > I thought that was one of the points of using first class functions > > (ducking tomatoes) ;-) > > I knew that this was coming. ;-) Thanks for the explanation. e.r. |
From: Eddie R. <er...@bm...> - 2008-08-28 20:20:04
|
On Thu, 2008-08-28 at 17:51 +0200, Albert Graef wrote: > Eddie Rucker wrote: > > Actually the thingy I sent was bad. (m, setter) should have been (m, > > set) like in the following. Since set is local to matrix and we have > > lexical scoping, I see no problem. > > Yes, I missed that. But you could still simplify your definition, by > just making gsl_matrix_set promote integer arguments to double. I don't > see why you need that type argument to the setter function at all. What about complex? So I still have to have a setter function for double and complex, so I might as well include integers. Maybe people studying combinatorics might need integer matrices, no? If I only had doubles, things would certainly be easier to implement. By the way, I have to go through the BLAS interface to multiply matrices so I guess I should do everything through the BLAS interface. I read somewhere that I should combine GLS with ATLAS because it is a faster BLAS. Oh well, I think I'm going to stick with double matrices first until I work out the quirks and then add in the complex and integer matrices. Now, if I could only find a portable way of determining the size of word boundaries for different machines and OSs ... I really hate to add a C wrapper on top of a C interface and then a Pure wrapper on top of the C wrapper but it's definitely looking to be the best route. e.r. |
From: Albert G. <Dr....@t-...> - 2008-08-28 22:24:14
|
Eddie Rucker wrote: > What about complex? So I still have to have a setter function for double > and complex, so I might as well include integers. Yes, sure. I didn't suggest that you should throw the int setter routine out of the window. You have an int setter routine that takes an int matrix and int values. Then you have a double setter routine which takes a double matrix and either an int (which gets promoted to double) or a double value. Etc., you get the idea. No need for that type argument to the setter routines. Coming to think of it, it's even better if you write a wrapper function for each type of matrix in C, which takes a list (as a pure_expr*) as argument and returns a matrix. That would execute *much* faster since you only have a single function call per matrix. (You don't initialize a big matrix with n^2 function calls in C either, or do you? If GSL doesn't provide a direct way to initialize a big matrix then that'd be a major performance hog.) Well, obviously I don't have an idea how you initialize a matrix in GSL. Can you point me to some C code which shows how to do this? Then I'll be able to sketch out how you can make it work using the Pure API. It should really be easy. > Now, if I could only find a portable way of determining the size of word > boundaries for different machines and OSs ... I still don't understand why you need that. Just to access the dimensions of the matrix? As I suggested earlier, all accesses to the matrix struct can and should be wrapped up in C routines, then you don't need that kind of hack. > I really hate to add a C > wrapper on top of a C interface and then a Pure wrapper on top of the C > wrapper but it's definitely looking to be the best route. Yes, it is. The Q modules work that way, too. When you have a complex C library, almost invariably you need some additional support routines for accessing the data, and that stuff is best written in C. Then you have a little Pure module which hides away all the complexity, loads the necessary C libraries and provides any high-level definitions (overloading operators etc.) that you want. All the Pure programmer ever sees is that Pure script. Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |