TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

A DSL in 5 Languages: Ruby, Python, PHP, C#, Java

60 pointsby pgr0ssalmost 15 years ago

16 comments

judofyralmost 15 years ago
A quick tips when creating block DSLs in Ruby:<p><pre><code> def search(&#38;blk) if blk.arity == 1 blk.call(self) else self.instance_eval(&#38;blk) end end # Then the user can decide what he want to use: Foo.search { bar } Foo.search { |s| s.bar }</code></pre>
评论 #1572161 未加载
Nyctoalmost 15 years ago
For the PHP version: Why is the search method static? You are forcing whatever class you are using to be tightly coupled. Dependency injection can be done in PHP, too.<p>Better:<p><pre><code> $bt = Braintree_Transaction_Search::someFactory(); $collection = $bt-&#62;search(array( $bt-&#62;orderId()-&#62;startsWith('a2d') $bt-&#62;customerWebsite()-&#62;endsWith('.com'), $bt-&#62;billingFirstName()-&#62;is('John'), $bt-&#62;status()-&#62;in(array( Braintree_Transaction::AUTHORIZED, Braintree_Transaction::SETTLED )), $bt-&#62;amount()-&#62;between('10.00', '20.00') )); </code></pre> If you're really up to it, there is no reason you couldn't use an even more fluent interface in PHP...<p><pre><code> $collection = $bt-&#62;where() -&#62;orderId()-&#62;startsWith('a2d') -&#62;and()-&#62;customerWebsite()-&#62;endsWith('.com') -&#62;and()-&#62;billingFirstName()-&#62;is('John') -&#62;search(); </code></pre> Also, please make sure the search method returns an iterator, not an array. Just because PHP allows you to shoot yourself in the foot, doesn't mean you should.
评论 #1573076 未加载
lispmalmost 15 years ago
In Lisp I would write a macro that could be used like this:<p><pre><code> (for-each (transaction) :where ((order :starts-with "a2d") (customer-website :ends-with ".com") (billing-first-name :equals "John") (status :one-of '(:authorized :settled)) (amount :between '("10.00" "20.00"))) :do (print (id transaction)))</code></pre>
评论 #1573086 未加载
评论 #1574097 未加载
postfuturistalmost 15 years ago
Python tip: instead of making the user contstruct a list literal, just use (star)args in the function definition. Python will automatically pack up any number of positional arguments into a list for you!<p><pre><code> def search(*args) : ... # args is a sequence search(this, that, the_other_thing) </code></pre> Edit: found the HN FAQ entry on formatting comments, finally.
评论 #1572072 未加载
评论 #1573798 未加载
评论 #1572164 未加载
评论 #1575353 未加载
评论 #1572090 未加载
评论 #1572006 未加载
kevingaddalmost 15 years ago
Not using 'var' (or LINQ, for that matter) in C# seems an odd choice. 'Separate steps for creating the request and performing the search' seems more like an advantage (for Java and C#) to me than a disadvantage, since you can easily eliminate the overhead with a helper function and the separation of a request and a search means that you can reuse a single request instance if you see the need.<p>I also consider statements like 'search.amount.between "10.00", "20.00"' or 'Amount.Between(10.00M, 20.00M)' to be vastly inferior to the native alternatives: for example, in C#, you could write that predicate as '(amount) =&#62; (amount &#62; 10.00M &#38;&#38; amount &#60; 20.00M)' and in Python, it would become the even shorter 'lambda amount : 10 &#62; amount &#62; 20'. In each case the native way of expressing the logic requires no special knowledge of the 'fluent' API.
评论 #1571842 未加载
评论 #1572066 未加载
jeswinalmost 15 years ago
Fluent interfaces are cool, but they throw away the semantics of the programming language like meaningful return values.<p>The DSL in the article is a good example of this problem:<p><pre><code> OrderId.StartsWith("a2d")...... </code></pre> So the StartsWith() returns a TransactionSearch, instead of a bool as you would expect. Once you do this often enough, you really get objects full of state-information, rather than methods with return values.<p>An alternative to doing this is using Expression Trees, but somewhat harder (and not without faults) It would look like this:<p><pre><code> search.Where(s =&#62; s.Order.Id.StartsWith('a2d') &#38;&#38; s.Customer.Website.EndsWith('.com') &#38;&#38; ...) </code></pre> Two benefits come to mind: 1. StartsWith() can return a bool as you would expect. 2. You can use StartsWith() on strings, like OrderId --- I am not sure how you pulled this off in the DSL example (is OrderId a custom type, since you can't use Extension methods there?)<p>The biggest drawback of course is: 1. Harder - You will need to parse the Expression Tree.
评论 #1572093 未加载
i2almost 15 years ago
You can make it without any 'weaknesses' in Python:<p><pre><code> collection = Transaction.search( order_id__starts_with='a2d', customer_website__ends_with='.com', billing__first_name__exact='John', status__in=[ Transaction.Status.Authorized, Transaction.Status.Settled ], amount__between=("10.00", "20.00") ) </code></pre> the implementation could look like this:<p><pre><code> def search(**kwargs): for arg, value in kwargs.items(): action = arg.split('__') attr = getattr(self, action[0]) if len(action) == 2: method = getattr(attr, action[1]) method(value) etc. ..... </code></pre> EDIT: removed unneeded quotes, thanks for correction, postfuturist
评论 #1575381 未加载
评论 #1572217 未加载
评论 #1573808 未加载
extensionalmost 15 years ago
In Java, I would do this:<p><pre><code> new Search().equal(Search.NAME,"Joe") .between(Search.AGE,18,25) .go(); </code></pre> Just as concise with less voodoo. And it's a single statement.<p>With the existing method, you can still ditch the empty parens by using public final fields. You just have to pre-populate them all with respective grammar objects.
评论 #1571944 未加载
d0malmost 15 years ago
I really enjoy reading these articles comparing different implementations in different languages. I wish we had more of that on HN.<p>Concerning the article, call me grumpy if you want, I like better when there is no mass overloading even thought it's more verbose. From my maintenance experience working on various project, I always cry when after 3 hours of searching I find that "+" is overloaded and that's where the bug was hidden.<p>Also, for this particular implementation, we could simply use a builder.<p>SearchBuilder sb;<p>sb.equal(Search.Name, ".."); sb.equals(Search.AGE, ..);<p>Search s = new Search(sb);<p>Even thought it's more verbose, we clearly see that we configure the builder as we want, and then, we create the search object. A good side effect of that is that we get an immutable Search object. Also, we could use the "fluent" interface on the builder.<p>Another verbose approach might be using lots of Objects..<p>Search s; s.add_contraint(SearchEqual(Search.Name, "bob"));<p>This way, it make it easier to add constraint without modifying the Search class.
评论 #1571989 未加载
psadauskasalmost 15 years ago
Let's clean up the Ruby example a bit, shall we?<p><pre><code> collection = Braintree::Transaction.search do order_id.begins_with? "a2d" customer_website.ends_with? ".com" billing_first_name == "John" status.in? Status::Authorized, Status::Settled amount.between? "10.00", "20.00" end collection.each do |transaction| puts transaction.id end</code></pre>
评论 #1572855 未加载
评论 #1574443 未加载
roryokanealmost 15 years ago
In the Ruby code, you include<p><pre><code> search.status.in( Braintree::Transaction::Status::Authorized, Braintree::Transaction::Status::Settled ) </code></pre> , but wouldn’t it be better to allow just<p><pre><code> search.status.in(:authorized, :settled) </code></pre> ? (And I agree with judofyr’s comment that “search.” shouldn’t be necessary on every line.)
postfuturistalmost 15 years ago
You can also handle a variable number of positional arguments in PHP instead of making the client send in a literal array.<p><pre><code> function search() { $arg_list = func_get_args(); ... }</code></pre>
devcjohnsonalmost 15 years ago
I'm confused in that none of the examples look like a DSL to me. They look like a library API - implemented in five different languages but I can hardly classify any of them as a DSL. Am I really that obtuse concerning this question?
kjbekkelundalmost 15 years ago
What about doing it like this in Ruby:<p><pre><code> collection = Braintree::Transaction.search do order_id /^a2d/ customer_website /\.com$/ billing_first_name "John" status :authorized, :settled amount 10..20 end collection.each do puts id end </code></pre> It might be more difficult not having the specific methods (in, starts_with_ ends_with, and so on), but I think it's quite readable. If we don't want the regexps, we could go for a MySQL style '%.com' and 'a2d%' or something similar.
评论 #1573812 未加载
icodealmost 15 years ago
Why dont they just take a string as the argument to search() and parse it? They could use a very SQL like Syntax:<p><pre><code> $collection = Braintree_Transaction::search(" orderId LIKE 'a2d%' AND customerWebsite LIKE '%.com' AND billingFirstName='John' AND status() IN ('AUTHORIZED','SETTLED') AND amount&#62;=10 AND amount&#60;=20.00) ");</code></pre>
评论 #1572444 未加载
评论 #1572579 未加载
评论 #1573706 未加载
bsdemonalmost 15 years ago
Why no Scala?