www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Where should I put a `condp` like function?

reply "Idan Arye" <GenericNPC gmail.com> writes:
I'm replicating `condp` from 
Clojure(http://clojuredocs.org/clojure_core/1.2.0/clojure.core/condp). 
Basically, it's like a `switch` statement without lookup table, 
only it's an expression(so it returns a value) and you can choose 
your own predicate(the default will be "a == b"). For example:

     writeln(2.predSwitch!"a < b"(
         1, "less then 1",
         5, "less then 5",
         10, "less then 10",
         "greater or equal to 10"));

Now, I've got it working(the example actually compiles and prints 
"less then 5"), and I want to make a pull request to put it in 
Phobos(because it's a useful function) but I don't really know 
where to put it. None of the existing modules seems fit, and I 
don't want to open a new module(std.monad?) for a single function.

Any suggestions?
Apr 14 2013
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Idan Arye:

 (because it's a useful function)

I think you should explain why you think it's useful. Bye, bearophile
Apr 14 2013
parent Nick Treleaven <ntrel-public yahoo.co.uk> writes:
On 15/04/2013 06:00, H. S. Teoh wrote:
 Allowing arbitrary predicates and switch-as-expression allows you to
 write code that shows intent very clearly:

 	// In pseudo-D syntax
 	void fill(T)(T image, int x, int y) {
 		image[x,y] = switch {
 			case isFillable(image,x,y): fillColor;
 			case isBorder(image,x,y): borderColor;
 			default: defaultColor;
 		};
 	}

We could use a conditional operator chain: image[x,y] = isFillable(image,x,y) ? fillColor : isBorder(image,x,y) ? borderColor : defaultColor;
Apr 17 2013
prev sibling next sibling parent "Idan Arye" <GenericNPC gmail.com> writes:
On Sunday, 14 April 2013 at 23:54:57 UTC, bearophile wrote:
 Idan Arye:

 (because it's a useful function)

I think you should explain why you think it's useful. Bye, bearophile

Well, `predSwitch` has two main advantages on regular `switch`. The first is being able to choose your own predicate. `switch` is preferable to a chain of `if`-`else if` because it's a clearer syntax(and because of the usage of lookup tables, but `predSwitch` does not have that), but not all `if`-`else if` chains are about simple equality checking - sometimes you need to check for other things, like which collection contains a value. Choosing your own predicate is helpful for that. The second advantage - which I consider much more important - it that `predSwitch` returns a value. This means you can use it mid-expression - for example, to initialize a constant, or to determine a function argument. You can't do those things with `switch` because it's a statement. If you try to initialize a constant in a `switch` statement, the constant's scope will be limited to the `case` where it is defined, and if you want to use `switch` to determine a function argument, you're gonna have to write the rest of the function call in each `case`.
Apr 14 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Idan Arye:

 You can't do those things with `switch` because it's a 
 statement.

In various Reddit threads I see people almost angry against the statement-expression distinction in contemporary programming languages. I am just starting to understand them. Bye, bearophile
Apr 14 2013
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Mon, 15 Apr 2013 02:48:27 +0200
"bearophile" <bearophileHUGS lycos.com> wrote:

 Idan Arye:
 
 You can't do those things with `switch` because it's a 
 statement.

In various Reddit threads I see people almost angry against the statement-expression distinction in contemporary programming languages. I am just starting to understand them.

I've never seen a big problem with the statement vs expression distinction, and I think the "statements == expresions" languages sometimes takes things slightly overboard in the process of forcing them into the same mold. However, I've *definitely* wished on many occasions that D's switch could be used as an expression like in Haxe.
Apr 14 2013
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sun, Apr 14, 2013 at 11:46:04PM -0400, Nick Sabalausky wrote:
 On Mon, 15 Apr 2013 02:48:27 +0200
 "bearophile" <bearophileHUGS lycos.com> wrote:
 
 Idan Arye:
 
 You can't do those things with `switch` because it's a 
 statement.

In various Reddit threads I see people almost angry against the statement-expression distinction in contemporary programming languages. I am just starting to understand them.


I think C was one of the early innovators in treating all function calls as expressions (or equivalently, allowing function calls to be statements). Many languages of that era differentiated between functions and procedures (== void functions), and treat calls to the latter strictly as statements.
 I've never seen a big problem with the statement vs expression
 distinction, and I think the "statements == expresions" languages
 sometimes takes things slightly overboard in the process of forcing
 them into the same mold. However, I've *definitely* wished on many
 occasions that D's switch could be used as an expression like in Haxe.

Allowing arbitrary predicates and switch-as-expression allows you to write code that shows intent very clearly: // In pseudo-D syntax void fill(T)(T image, int x, int y) { image[x,y] = switch { case isFillable(image,x,y): fillColor; case isBorder(image,x,y): borderColor; default: defaultColor; }; } This can help readability a lot when the outer expression is complicated. It's reminiscient of Dijkstra's guarded command language, which has a condition statement that contains a bunch of predicate-statement pairs; during execution, one statement is chosen from the conditional block whose predicate evaluates to true. At least one predicate must be true at any time, otherwise it is an error (similar to D's final switches). If more than one predicate evaluates to true, the choice is non-deterministic. The implementation can choose to provide a built-in uniform randomizer for this case. This lets you state the preconditions of statements up-front, thereby reducing mistakes caused by implicit assumptions that fail to hold. With code that is continually being revised, this can help prevent a lot of bugs. T -- Fact is stranger than fiction.
Apr 14 2013
prev sibling next sibling parent "Idan Arye" <GenericNPC gmail.com> writes:
On Monday, 15 April 2013 at 03:46:47 UTC, Nick Sabalausky wrote:
 On Mon, 15 Apr 2013 02:48:27 +0200
 "bearophile" <bearophileHUGS lycos.com> wrote:

 Idan Arye:
 
 You can't do those things with `switch` because it's a 
 statement.

In various Reddit threads I see people almost angry against the statement-expression distinction in contemporary programming languages. I am just starting to understand them.

I've never seen a big problem with the statement vs expression distinction, and I think the "statements == expresions" languages sometimes takes things slightly overboard in the process of forcing them into the same mold. However, I've *definitely* wished on many occasions that D's switch could be used as an expression like in Haxe.

Having D's `switch` be an expression is problematic, since D does not have the convention of blocks-returning-the-value-of-the-last-statement, and since D is statically typed - adding this might cause unwanted implicit conversions. Maybe it could be pulled off with a syntax similar to Scala: switch(x){ case 1 => ... case 2 => ... } This will also make it look similar to the function literal style, where using `=>` means you are gonna write the function's body as the return expression instead of as a block. In the meanwhile, we can use `predSwitch` - if I only knew where to put it...
Apr 15 2013
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Sun, 14 Apr 2013 22:00:18 -0700
"H. S. Teoh" <hsteoh quickfur.ath.cx> wrote:

 On Sun, Apr 14, 2013 at 11:46:04PM -0400, Nick Sabalausky wrote:
 I've never seen a big problem with the statement vs expression
 distinction, and I think the "statements == expresions" languages
 sometimes takes things slightly overboard in the process of forcing
 them into the same mold. However, I've *definitely* wished on many
 occasions that D's switch could be used as an expression like in
 Haxe.

Allowing arbitrary predicates and switch-as-expression allows you to write code that shows intent very clearly: // In pseudo-D syntax void fill(T)(T image, int x, int y) { image[x,y] = switch { case isFillable(image,x,y): fillColor; case isBorder(image,x,y): borderColor; default: defaultColor; }; }

Yup, like I said, I've definitely wished that D's switch could be used as an expression
Apr 15 2013
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Mon, 15 Apr 2013 18:41:07 +0200
"Idan Arye" <GenericNPC gmail.com> wrote:
 
 Having D's `switch` be an expression is problematic, since D does 
 not have the convention of 
 blocks-returning-the-value-of-the-last-statement, and since D is 
 statically typed - adding this might cause unwanted implicit 
 conversions.
 
 Maybe it could be pulled off with a syntax similar to Scala:
      switch(x){
          case 1 => ...
          case 2 => ...
      }
 

Wouldn't it be possible to just simply choose between these two forms based on whether the switch is used where an expression is expected versus where a statement would be accepted?: switch(cond) { case 1: [...statements...] break; ... } vs switch(cond) { case 1: [...expression...]; ... }
Apr 15 2013
prev sibling next sibling parent "Idan Arye" <GenericNPC gmail.com> writes:
On Monday, 15 April 2013 at 17:07:22 UTC, Nick Sabalausky wrote:
 On Mon, 15 Apr 2013 18:41:07 +0200
 "Idan Arye" <GenericNPC gmail.com> wrote:
 
 Having D's `switch` be an expression is problematic, since D 
 does not have the convention of 
 blocks-returning-the-value-of-the-last-statement, and since D 
 is statically typed - adding this might cause unwanted 
 implicit conversions.
 
 Maybe it could be pulled off with a syntax similar to Scala:
      switch(x){
          case 1 => ...
          case 2 => ...
      }
 

Wouldn't it be possible to just simply choose between these two forms based on whether the switch is used where an expression is expected versus where a statement would be accepted?: switch(cond) { case 1: [...statements...] break; ... } vs switch(cond) { case 1: [...expression...]; ... }

Possible? probably. Simply? probably not. `switch` is already a statement, so it will have to be added as an expression, but expressions in D can be used as statements, so it could create an ambiguity in the syntax parser - and those are seldom easy to solve. The `switch`+`=>` solution should be simpler, since it doesn't create that ambiguity, but syntax additions are never that simple. `predSwitch`, on the other hand, is dead simple.
Apr 15 2013
prev sibling next sibling parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Mon, 15 Apr 2013 01:52:33 +0200
"Idan Arye" <GenericNPC gmail.com> wrote:
 but I don't really know 
 where to put it. None of the existing modules seems fit, and I 
 don't want to open a new module(std.monad?) for a single function.
 
 Any suggestions?

I'm not a Phobos dev, but I think std.algorithm would be the right place. You can always just pick a spot, make a pull request, and if there's a better place then people can just say so in the pull request's discussion and then you can move it.
Apr 15 2013
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 04/16/2013 04:39 AM, Idan Arye wrote:
 On Monday, 15 April 2013 at 18:02:14 UTC, Nick Sabalausky wrote:
 ...
 I'm not a Phobos dev, but I think std.algorithm would be the right
 place.

 You can always just pick a spot, make a pull request, and if there's
 a better place then people can just say so in the pull request's
 discussion and then you can move it.

Good idea. It's not really an algorithm - but then again, many of the things in std.algorithm are not really algorithms,

Well, given some obvious constraints on the inputs, they are.
 and could be easy fit
 to a separate module, called std.monad.

 Ofcourse, I do not suggest to split std.algorithm to std.algorithm and
 std.monad - that would break too much code...

I do not see how std.monad would be an accurate name in any case.
Apr 16 2013
prev sibling parent David Gileadi <gileadis NSPMgmail.com> writes:
On 4/15/13 11:05 AM, H. S. Teoh wrote:
 Without geometry, life would be pointless. -- VS

It's this kind of joke where I draw the line :)
Apr 16 2013
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Apr 15, 2013 at 02:01:28PM -0400, Nick Sabalausky wrote:
 On Mon, 15 Apr 2013 01:52:33 +0200
 "Idan Arye" <GenericNPC gmail.com> wrote:
 but I don't really know 
 where to put it. None of the existing modules seems fit, and I 
 don't want to open a new module(std.monad?) for a single function.
 
 Any suggestions?

I'm not a Phobos dev, but I think std.algorithm would be the right place.

Hmm. What about std.functional? It seems to fall into the category of stuff that lets/helps you write functional-style D code. T -- Without geometry, life would be pointless. -- VS
Apr 15 2013
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Mon, 15 Apr 2013 11:05:05 -0700
"H. S. Teoh" <hsteoh quickfur.ath.cx> wrote:

 On Mon, Apr 15, 2013 at 02:01:28PM -0400, Nick Sabalausky wrote:
 On Mon, 15 Apr 2013 01:52:33 +0200
 "Idan Arye" <GenericNPC gmail.com> wrote:
 but I don't really know 
 where to put it. None of the existing modules seems fit, and I 
 don't want to open a new module(std.monad?) for a single function.
 
 Any suggestions?

I'm not a Phobos dev, but I think std.algorithm would be the right place.

Hmm. What about std.functional? It seems to fall into the category of stuff that lets/helps you write functional-style D code.

Yea, but the same is true for a lot of std.algorithm. Besides, despite the name, std.functional appears to mostly just be the place for the tools to use the old string literal style lambdas.
Apr 15 2013
prev sibling next sibling parent "Idan Arye" <GenericNPC gmail.com> writes:
On Monday, 15 April 2013 at 18:02:14 UTC, Nick Sabalausky wrote:
 On Mon, 15 Apr 2013 01:52:33 +0200
 "Idan Arye" <GenericNPC gmail.com> wrote:
 but I don't really know where to put it. None of the existing 
 modules seems fit, and I don't want to open a new 
 module(std.monad?) for a single function.
 
 Any suggestions?

I'm not a Phobos dev, but I think std.algorithm would be the right place. You can always just pick a spot, make a pull request, and if there's a better place then people can just say so in the pull request's discussion and then you can move it.

Good idea. It's not really an algorithm - but then again, many of the things in std.algorithm are not really algorithms, and could be easy fit to a separate module, called std.monad. Ofcourse, I do not suggest to split std.algorithm to std.algorithm and std.monad - that would break too much code...
Apr 15 2013
prev sibling next sibling parent "Idan Arye" <GenericNPC gmail.com> writes:
On Monday, 15 April 2013 at 18:30:26 UTC, Nick Sabalausky wrote:
 On Mon, 15 Apr 2013 11:05:05 -0700
 "H. S. Teoh" <hsteoh quickfur.ath.cx> wrote:

 On Mon, Apr 15, 2013 at 02:01:28PM -0400, Nick Sabalausky 
 wrote:
 On Mon, 15 Apr 2013 01:52:33 +0200
 "Idan Arye" <GenericNPC gmail.com> wrote:
 but I don't really know where to put it. None of the 
 existing modules seems fit, and I don't want to open a new 
 module(std.monad?) for a single function.
 
 Any suggestions?

I'm not a Phobos dev, but I think std.algorithm would be the right place.

Hmm. What about std.functional? It seems to fall into the category of stuff that lets/helps you write functional-style D code.

Yea, but the same is true for a lot of std.algorithm. Besides, despite the name, std.functional appears to mostly just be the place for the tools to use the old string literal style lambdas.

Actually, std.functional only has two functions that turn a string to a function. Most of std.functional's members are for creating functions from other functions.
Apr 15 2013
prev sibling next sibling parent reply "Graham Fawcett" <fawcett uwindsor.ca> writes:
On Sunday, 14 April 2013 at 23:52:37 UTC, Idan Arye wrote:
 Now, I've got it working(the example actually compiles and 
 prints "less then 5"), and I want to make a pull request to put 
 it in Phobos(because it's a useful function) but I don't really 
 know where to put it. None of the existing modules seems fit, 
 and I don't want to open a new module(std.monad?) for a single 
 function.

Nit-picky observation: there's nothing monadic about your predSwitch function, it wouldn't belong in a 'std.monad' module. Best, Graham
Apr 18 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 04/18/2013 04:18 PM, Idan Arye wrote:
 On Thursday, 18 April 2013 at 13:04:19 UTC, Graham Fawcett wrote:
 On Sunday, 14 April 2013 at 23:52:37 UTC, Idan Arye wrote:
 Now, I've got it working(the example actually compiles and prints
 "less then 5"), and I want to make a pull request to put it in
 Phobos(because it's a useful function) but I don't really know where
 to put it. None of the existing modules seems fit, and I don't want
 to open a new module(std.monad?) for a single function.

Nit-picky observation: there's nothing monadic about your predSwitch function, it wouldn't belong in a 'std.monad' module. Best, Graham

A monad is "a structure that represents computations"(quote from Wikipedia). ...

Read on.
Apr 18 2013
prev sibling next sibling parent "Idan Arye" <GenericNPC gmail.com> writes:
On Wednesday, 17 April 2013 at 13:09:45 UTC, Nick Treleaven wrote:
 On 15/04/2013 06:00, H. S. Teoh wrote:
 Allowing arbitrary predicates and switch-as-expression allows 
 you to
 write code that shows intent very clearly:

 	// In pseudo-D syntax
 	void fill(T)(T image, int x, int y) {
 		image[x,y] = switch {
 			case isFillable(image,x,y): fillColor;
 			case isBorder(image,x,y): borderColor;
 			default: defaultColor;
 		};
 	}

We could use a conditional operator chain: image[x,y] = isFillable(image,x,y) ? fillColor : isBorder(image,x,y) ? borderColor : defaultColor;

Naturally - even as statements, a `switch` statement without a switch expression is no better than a chain of `if`-`else`s. Now, consider this hackish use of `predSwitch`: https://gist.github.com/someboddy/5412843 This is actual D code(you need https://github.com/D-Programming-Language/phobos/pull/1259 to run it), and it prints: ***** *...* *.+.* *...* ***** As expected. This does have an advantage over a chain of `if`-`else`s or ternary operators - you only need to write the argument list once.
Apr 18 2013
prev sibling next sibling parent "Idan Arye" <GenericNPC gmail.com> writes:
On Thursday, 18 April 2013 at 13:04:19 UTC, Graham Fawcett wrote:
 On Sunday, 14 April 2013 at 23:52:37 UTC, Idan Arye wrote:
 Now, I've got it working(the example actually compiles and 
 prints "less then 5"), and I want to make a pull request to 
 put it in Phobos(because it's a useful function) but I don't 
 really know where to put it. None of the existing modules 
 seems fit, and I don't want to open a new module(std.monad?) 
 for a single function.

Nit-picky observation: there's nothing monadic about your predSwitch function, it wouldn't belong in a 'std.monad' module. Best, Graham

A monad is "a structure that represents computations"(quote from Wikipedia). While the Turing machine branch(=the main branch) of computability theory quickly fell into the Turing tarpit and had to moved to pseudo-code, the Lambda calculus was expended using the traditional mathematical method of defining symbols that replace other symbols. That's why when in the Turing machine branch you could just write `if ... then ... else ...` and it's okay because it's pseudo-code, in Lambda calculus you actually have an IFTHENELSE defined as a lambda expression. And so on with loops, exception handling, and even type definitions and input/output. I find that `predSwitch` - a function that represents the `switch` idiom - perfectly fits into the definition of monads.
Apr 18 2013
prev sibling next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Thursday, 18 April 2013 at 13:58:46 UTC, Idan Arye wrote:
 Naturally - even as statements, a `switch` statement without a 
 switch expression is no better than a chain of `if`-`else`s.

As mentioned before in other thread, LLVM is crazy good at transforming if/else chains into switch statement, and GCC does a good job at it as well.
Apr 18 2013
prev sibling next sibling parent "Idan Arye" <GenericNPC gmail.com> writes:
On Thursday, 18 April 2013 at 14:26:08 UTC, deadalnix wrote:
 On Thursday, 18 April 2013 at 13:58:46 UTC, Idan Arye wrote:
 Naturally - even as statements, a `switch` statement without a 
 switch expression is no better than a chain of `if`-`else`s.

As mentioned before in other thread, LLVM is crazy good at transforming if/else chains into switch statement, and GCC does a good job at it as well.

A `switch` statement is faster than a `if`-`else` chain only when it can use a lookup table, and for the lookup table a switch expression is a must. Another precondition for a lookup table is that the test expressions are known at compile-time. The given example has none - there is no switch expression(though one may argue that you can use `true` as the switch expression) and the test expressions are function calls calculated at runtime. If the compiler can translate a `if`-`else` chain to a `switch` statement - then it should have probably been a `switch` statement in the first place. That way it would have also been more readable.
Apr 18 2013
prev sibling next sibling parent "Idan Arye" <GenericNPC gmail.com> writes:
On Thursday, 18 April 2013 at 17:12:51 UTC, Timon Gehr wrote:
 On 04/18/2013 04:18 PM, Idan Arye wrote:
 On Thursday, 18 April 2013 at 13:04:19 UTC, Graham Fawcett 
 wrote:
 On Sunday, 14 April 2013 at 23:52:37 UTC, Idan Arye wrote:
 Now, I've got it working(the example actually compiles and 
 prints
 "less then 5"), and I want to make a pull request to put it 
 in
 Phobos(because it's a useful function) but I don't really 
 know where
 to put it. None of the existing modules seems fit, and I 
 don't want
 to open a new module(std.monad?) for a single function.

Nit-picky observation: there's nothing monadic about your predSwitch function, it wouldn't belong in a 'std.monad' module. Best, Graham

A monad is "a structure that represents computations"(quote from Wikipedia). ...

Read on.

OK, so it's not a formal monad, but it has the same idea of - "an action that chooses the next action based on the results of previous actions" - for the same purpose - "insert additional operations around a program's domain logic".
Apr 19 2013
prev sibling next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Friday, 19 April 2013 at 19:02:51 UTC, Idan Arye wrote:
 OK, so it's not a formal monad, but it has the same idea of - 
 "an action that chooses the next action based on the results of 
 previous actions" - for the same purpose - "insert additional 
 operations around a program's domain logic".

Monad don't perform action. They describe an action to be performed by something else. Usually a lib or the runtime (as in Haskell).
Apr 19 2013
prev sibling parent "Idan Arye" <GenericNPC gmail.com> writes:
On Friday, 19 April 2013 at 19:05:10 UTC, deadalnix wrote:
 On Friday, 19 April 2013 at 19:02:51 UTC, Idan Arye wrote:
 OK, so it's not a formal monad, but it has the same idea of - 
 "an action that chooses the next action based on the results 
 of previous actions" - for the same purpose - "insert 
 additional operations around a program's domain logic".

Monad don't perform action. They describe an action to be performed by something else. Usually a lib or the runtime (as in Haskell).

Oh, I see. So I guess I got it wrong...
Apr 19 2013