On Wed, Jan 31, 2007 at 09:28:30PM +0300, Bulat Ziganshin wrote: > Wednesday, January 31, 2007, 7:12:05 PM, you wrote: > >> data Coord = Coord Float Float > >> view of Coord = Polar Float Float where > >> Polar r d = Coord (r*d) (r+d) -- construction > >> Coord x y | x/=0 || y/=0 = Polar (x*y) (x+y) -- matching > > > This is somewhat pretty, but in spite of your desire to avoid creating new > > syntax, you have just done so, and in the process made views more limited. > > Pattern matching sytax remains the same, but a new declaration syntax has > > been added. And now in order to pattern match on a function it needs to > > explicitely be declared as a "view". > > yes. among the possible uses for views i clearly prefers the following: > definition of abstract data views that may differ from actual type > representation. moreover, i think that this facility should be > syntactically indistinguishable from ordinary data constructor patterns > in order to simplify learning and using of language. *defining* view is a > rare operation, using it - very common so my first point is that views > should be used in just the same way as ordinary constructors, both on > left and right side: > > f (Polar r a) = Polar (r*2) a
I guess your assumption that views will rarely be defined is rather in conflict with the proposal of Simon, which was to create considerably more powerful views, I presume. > Next, i don't think that ability to use any functions in view buy > something important. pattern guards can be used for arbitrary > functions, or such function can be used in view definition. view, > imho, is not a function - it's a two-way conversion between abstract > and real data representation which has one or more alternative > variants - just like Algebraic Data Types. so, when defining a view, i > want to have ability to define exactly all variants alternative to > each other. for another representation, another view should be > created. so But you *are* using functions in views, that's what they are. And the two-way conversion, while pretty, is likely to be a fiction. It'll be too easy (and useful) for someone to define view RegexpMatch String of String where string | matchesRegexp regexp string = RegexpMatch regexp RegexpMatch regexp = undefined f (RegexpMatch "foo.+bar") = "It has foo bar in it" f s@(RegexpMatch "baz.+bar") = s ++ " has baz bar in it" You can pretend that noone will do this, but it's a nice syntax for pattern guards, which allows us to stick the guard right next to the data being guarded, which is often handy. So I guess you can see this as a promise to subvert your two-way conversion views immediately after they're created. It's worth considering whether one should try to make the syntax friendlier to such uses. One option which is sort of in between would be something like: view regexpMatch String of String where string | matchesRegexp regexp string = regexpMatch regexp f (regexpMatch "foo.+bar") = "It has foo bar in it" f s@(regexpMatch "baz.+bar") = s ++ " has baz bar in it" where the lowercaseness of "regexpMatch" indicates that this is a one-way matching function. I believe this would work just fine, and then we'd have a bit of new syntax for "function-like" views, and your constructor-like syntax for "constructor-like" views. And noone would be tempted to subvert your constructor-like views. And good programmers would have a policy that constructor-like views would really be invertible, for some definition of invertible, analogous to the monad laws, which aren't enforced, but reasonable programmers obey. > view Polar Float Float of Coord where > constructor (Polar r a) means (Coord (r*sin a) (r*cos a)) > match pattern (Polar (sqrt(x*x+y*y)) (atan(y/x))) for (Coord x y) where x/=0 > (Polar y (pi/2)) for (Coord x y) where y>0 > (Polar (-y) (-pi/2)) for (Coord x y) where y<0 > > of course, my syntax is cumbersome. that is important is that view > definition should be explicit (no arbitrary functions), it should > mention all possible alternatives and provide a way to use the same > constructor name both for construction of new values and matching > existing ones. this all together should allow to transparently use ADT > views instead of plain ADTs I definitely agree that being able to transparently switch a library between views and exported constructors would be handy, but don't think it's necesary, provided the view syntax is sufficiently elegant (which I'm not convinced Simon's proposed syntax is). If views have a distinct--but pretty--syntax, people can just move to always using views, and that's that. > > And unless you are planning to allow one-way views (you don't give any > > examples of that), "view functions" must be invertible, which greatly > > weakens their power. If you choose to allow one-way views (non-invertible > > functions), then I'd vote for not allowing two-way views, as it adds > > complexity without adding any appreciable gain. > > > I don't like your use of capital letters for ordinary functions, I enjoy > > having the syntax tell me whether (Foo 1) might or might not be an > > expensive operation. > > the whole idea of abstraction is to not give users any knowledge aside > from algorithmic specifications. when you write (x+y) you don't know > whether this (+) will end in ADD instruction or sending expedition to > Mars :) why you need low-level control over data matchers exported by > library but not over its functions? Granted. It's not necesary, but I find that it can be handy to have a bit of syntactic information about cost. > > Finally, you've replaced Simon's explicit incomplete function using Maybe > > with an implicit incomplete function that returns _|_ when the view doesn't > > match. > > it's an independent idea that can be used for Simon's syntax or don't > used at all. really, we need Prolog-like backtracking mechanism, i.e. > way to say "this pattern don't match input value, please try the next > alternative". Simon emulated backtracking with Maybe, one can does the > same with return/fail, i figured out one more way - just allow > recursive use of function guards. I am less troubled about this than I was before, but I still don't like the implied inequivalence. I like to assume that pattern matching and guards can be translated into if statements with error, and with your syntax I'm not sure whether this is true. i.e. what happens if I wrote data Coord = Coord Float Float view of Coord = Polar Float Float where Polar r d = Coord (r*d) (r+d) -- construction Coord x y = if x /= 0 && y /= 0 then Polar (x*y) (x+y) -- matching else undefined -- not matching Is this an invalid bit of code, i.e. for your views syntax, are you reusing only a subset of the function syntax? Or is this valid code that causes an error when you match f (Polar r a) = ... if the argument is Coord 0 0? If we don't allow this syntax (if statement on the RHS of the matching definition), why not? The syntax you propose can be used to decribe arbitrary functions, so why not allow us coders to use the ordinary Haskell syntax to define these functions, rather than a subset thereof? I have a feeling that with complicated views, coding the entire function on the LHS could get cumbersome pretty quickly. Of course, with pattern guards, one can always get around this by defining a helper function, but I'd prefer to avoid syntax constraints that require that I define a helper function that's only used once. -- David Roundy Department of Physics Oregon State University _______________________________________________ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime