From: James Edward Gray II Date: 2007-06-06T06:22:25+09:00 Subject: Re: Gecode/R - Request for syntax feedback On Jun 5, 2007, at 2:43 PM, Andreas Launila wrote: > == Issues == > > === Describing (linear) constraints === > A problem with the above is that people might look at it and > instinctively think that it's something that does not have a side > effect. Therefore prepending some sort of method might convey that > intention better. For instance we could allow people to write the > following. > > constrain x + y == z RSpec works a lot like this. It has you define expectations with syntax like: some_obj.should equal(whatever) some_obj.should_not equal(whatever) > Fitting words other than "constrain" might be "assert", "post" and > "add". I like the word "constrain." If you go the RSpec route though, you will probably also want a negative form and I don't know what that would be. > One could also go a bit further and make using such a prepended > method compulsory for consistency. I think that's a sound strategy. That's pretty much the point of should/should_not in RSpec. It gives the user a visual queue that says something like, "We're now switching into defining constraints mode." > I guess the real question is how > disturbing it is for an average Ruby programmer to have arithmetic > methods that have side effects. Do you see it as natural, > disturbing or > something in between? I don't think it's too shocking. I think most programmers can accept that objects can override methods. You see that the variables are created as IntVar objects, and it's not a far leap that IntVar redefines some operators. If I had to choose though, I think I prefer the RSpec-like context changing method prefix. > One drawback with it is consistency. Since != can't be redefined to > be something other than !(x == y) inequality has to be treated > differently. I do like consistency, so, for what it's worth, I prefer equal/ not_equal since we can't have both == and !=. > === Variable access === > One way that should be familiar is to have the user save squares in an > instance variable (e.g. @squares) that can then be accessed other > where. This feels pretty natural to me. It works on the knowledge we already have. > Yet another would be to have the user explicitly declare variables > (possibly on a class level), which would then be accessible > anywhere in the instance. What would you suggest? This could be another interesting option. See my comment below about a DSL... > === Describing distinct constraints === > Other ways of specifying the constraint could also be imagined, > such as "squares.all_distinct". The latter could make things a bit > more consistent by allowing a method to be prepended, such as > "constrain squares.all_distinct" (assuming such a prepended method > is used for the linear constraints). What feels more natural/readable? I worry that the syntax purposed above requires you to add some methods to Array and/or Matrix. This seems like a slope you want to avoid. As an idea, again using RSpec for inspiration: constrain distinct(some_enum) > === Overall syntax === > > Every model has at least three parts: > * Declaration of variables (in the example that would be "squares > = ...") > * Definition of constraints (all the lines from "all_distinct ..." > down > to "squares.diagonal..." in the example). > * Selection of branching strategy (the line starting with > "branch_on" in > the example) > > Maybe that could be used to produce some other overall syntax? E.g. > something other than throwing it all into a constructor. Reading this definitely made me wonder over a DSL. That's pretty much what you have with the class system though. If constrain() becomes the constraint definer and branch_on() represents the branching strategy, the only thing you haven't really wrapped is the variable declaration. I don't know what that would look like, but maybe something like: variable :some_name, :int, accepted_range_of_values Then I guess you could later refer to them by name. Of course, that's just not as convenient when it comes to accessing them. Iterating over rows and columns shows this well. Whatever way you go with this though, you may want to consider providing a DSL wrapper for your classes, something like: magic_square = problem do |model| # ... end That just might be nice when you don't need to define a full class. James Edward Gray II