I'm trying to create a custom operator that is a synonym for the dot (member access) operator, such that a>>=b results in a.b.
My first attempt does not compile
operator >>= left 19 = (left, right) => {
return #`${left}.${right}]`;
};
If i change . by && in the return statement, then it does compile.
Since the previous approach did not work out, i tried using computed member access:
operator >>= left 19 = (left, right) => {
let r = right.name.token.value; // string representation of an identifier
let dummy = #`dummy`.get(0);
return #`${left} [${fromStringLiteral(dummy, r)}]`;
};
This approach compiles for simple expressions like a>>=b, but not for a>>=b.then(res => ...), because right is now a call expression: b.then(...), which i don't understand since i chose a precedence of 19 for my custom operator, which is also the precedence of [] hence conceptually this means that my expression should be equivalent to (a>>b).then(res => ...) or am i wrong?
Related
My question
I'm referring to a function which does essentially the following (modulo const, &, perfect forwarding, or whatever is appropriate):
auto constexpr dollar = [](auto f, auto x){ return f(x); }; // Why calling it "dollar"? Keep reading...
Is such a function expressable only via Boost.Hana?
Why did I think of it?
In Haskell, such a function exists, and it's called ($) ($ in infix form), and its definition is the following (source code)
($) :: forall r a (b :: TYPE r). (a -> b) -> a -> b
f $ x = f x
and you could write the second line simply as either of the following
(f $) = f
($) f = f
where the second form makes it apparent that ($) is essentially the same as the id (identity function)
id :: a -> a
id x = x
just with a signature that enforces that the first argument be of function type a -> b and the second argument of type a.
Indeed, applying f to x in Haskell can be done also by writing this
f `id` x
i.e. using `id` instead of $.¹
How is that related to Hana?
Since Hana does offer an id function, I was wondering if that (maybe together with something else) can be used to define a function application utility without manually writing the lambda at the top of this post.
The difficult part
The hard part here is that when you write f `id` x in Haskell, there's not much really a point in arguing on whether you're passing 1 or 2 arguments to id, because all functions are curried by default.
That's not true in C++. For instance I can do this:
#include <boost/hana/functional/id.hpp>
#include <iostream>
using boost::hana::id;
int main() {
auto plus1 = [](int x){ return x + 1; };
std::cout << id(plus1)(3) << std::endl; // prints 4
}
which looks a lot like id is curried and is being given two inputs one after the other rather than together, but that's not true. It's just that id(plus1) is returning plus1, which is fed with 3. I don't know how to get the following (which would be equivalent to plus1 `id` 3 or id plus1 3 in Haskell) work:
std::cout << id(plus1, 3) << std::endl; // doesn't even compile obviously
The true origin of the puzzle
After reading To Mock a Mockingbird, I wondered: "How do I implement the Thrush in C++ only with Boost.Hana?" (And the Thrush is the boost::hana::flipped version of the function application operator.)
¹In reality it's not exactly the same if want to write chains of applications, as the two operators have different associativity, so f $ g $ x == f `id` (g `id` x), but this is not relevant to the question, I believe.
I am very new to Ocaml and ML in general and I have been having a very fundamental issue. I am using a pattern match and within one match I would like to print two or more concatenated statements. Eg.
chan^"("^var^")"^op2^(poc p); chan^"("^var^")"^op^(poc p)
let processoperatorchange2 t2s2 proc2 op op2=
let rec poc2 p = match p with
| Zero -> "0"
| Pproc (x) -> String.lowercase x
| In(chan, var, _, p, _) -> chan^"("^var^")"^op^(poc2 p); chan^"("^var^")"^op2^(poc2 p)
in poc2 proc2
But then each time I run this, the only statement printed is the last one after the semi colon. Can I get some help with this?
Your function does not print a statement but builds a string, thus it returns a value, and doesn't perform any side-effects. The semicolon operator, when interspersed between two expressions, doesn't combine the value produced from these expressions, thus if you have "hello"; "world" the result is "world". That is what happens in your case when you do
chan^"("^var^")"^op^(poc2 p); chan^"("^var^")"^op2^(poc2 p)
Everything on the lift is just thrown away.
A quick fix would be to concatenate them, e.g.,
chan^"("^var^")"^op^(poc2 p) ^ ";\n" ^ chan^"("^var^")"^op2^(poc2 p)
But in general, an idiomatic way to print AST is to use the Format module, and implement a recursive pp function, that has type Format.formatter -> 'a -> unit. Note the return type, the function doesn't build a string (that is usually an operation of quadratic complexity), but rather prints it into generic output stream.
Why does this compile?
fun foo (h::t) =
h = hd(t);
But this does not
fun foo (h::t) =
PolyML.print (h::t);
print "\n";
h = hd(t);
?
Value or constructor (h) has not been declared Found near =( h, hd(t))
Value or constructor (t) has not been declared Found near =( h, hd(t))
Exception- Fail "Static errors (pass2)" raised
I think your frustration with the language prevents you from solving your problem(s) more than the limitations of the language. As I said in a previous answer, semicolons cannot be used like you used them. You need to wrap those statements inside parentheses:
fun foo (h::t) =
(
PolyML.print (h::t);
print "\n";
h = hd(t)
)
Furthermore, you first snippet doesn't need a semicolon:
fun foo (h::t) =
h = hd(t)
Here's the thing, in SML semicolons are not used to terminate statements, they're used to separate expressions. Think of ; as a binary operator, just like + or -. With the added constraint that you need parentheses around.
Also, you're probably using the = operator in the wrong way inside h = hd(t). It's not assignment, it's an equality check, just like == in other languages. If you want assignment, you need a ref type.
It's probably better to ask what exactly you're trying to solve, because at this point you're totally misunderstanding the syntax and semantics of SML and we can't really write a tutorial on in here.
I know that OCaml does not support overloading. Then, instead of overloading, what can we do to work this around?
1) use polymorphism instead?
2) give different functions different names?
3) put functions of the same name in different modules?
Which one will work?
It all depends on what you mean by overloading. There are several use cases, such as:
If you want to use the usual infix operators name in a mathematical expression manipulating something else than integers: rebind your operators locally; modules and "local open" can help with that.
module I32 = struct
open Int32
let (+), (-), ( * ), (/), (!!) = add, sub, mul, div, of_int
end
... I32.(x + y * !!2) ...
If you want an operation to be polymorphic in the type of numeric type being used, you need to abstract over such numeric operators. For example the generic fast exponentiation function (by an integer), that can be used on matrices etc.
let rec pow ( * ) one a = function
| 0 -> one
| n -> pow ( * ) (if n mod 2 = 0 then one else one * a) (a * a) (n / 2)
let () = assert (pow ( *.) 1. 2. 3 = 8.)
More generally, yes, the idea is to capture what you want to "overload" on as a set of operators (here infix operators but plain names are fine and often better for readability), and pass around and abstract over dictionaries of those operations -- much like what Haskell type classes are compiled to, in fact.
In Scala, I can make a caseclass, case class Foo(x:Int), and then put it in a list like so:
List(Foo(42))
Now, nothing strange here. The following is strange to me. The operator :: is a function on a list, right? With any function with one argument in Scala, I can call it with infix notation.
An example is 1 + 2 is a function (+) on the object Int. The class Foo I just defined does not have the :: operator, so how is the following possible?
Foo(40) :: List(Foo(2))
In Scala 2.8 RC1, I get the following output from the interactive prompt:
scala> case class Foo(x:Int)
defined class Foo
scala> Foo(40) :: List(Foo(2))
res2: List[Foo] = List(Foo(40), Foo(2))
I can go on and use it, but what is the explanation?
From the Spec:
6.12.3 InfixOperations An infix operator can be an arbitrary
identifier. Infix operators have
precedence and associativity defined
as follows.
...
The associativity of an operator is
determined by the operator’s last
character. Operators ending in a colon
‘:’ are right-associative. All other
operators are left- associative.
You can always see how these rules are applied in Scala by printing the program after it has been through the 'typer' phase of the compiler:
scala -Xprint:typer -e "1 :: Nil"
val r: List[Int] = {
<synthetic> val x$1: Int = 1;
immutable.this.Nil.::[Int](x$1)
};
It ends with a :. And that is the sign, that this function is defined in the class to the right (in List class here).
So, it's List(Foo(2)).::(Foo(40)), not Foo(40).::(List(Foo(2))) in your example.
One aspect missing in the answers given is that to support :: in pattern matching expressions:
List(1,2) match {
case x :: xs => println(x + " " + xs)
case _ => println("")
}
A class :: is defined :
final case class ::[B](private var hd: B, private[scala] var tl: List[B])
so case ::(x,xs) would produce the same result. The expression case x :: xs works because the default extractor :: is defined for the case class and it can be used infix.
The class Foo I just defined does not
have the :: operator, so how is the
following possible:
Foo(40) :: List(Foo(2))
If the method name ends with a colon (:) the method is invoked on the right operand, which is the case here. If the method name doesn't end with colon, the method is invoked on the left operand. For example, a + b, + is invoked on a.
So, in your example, :: is a method on its right operand, which is a List.