Is it possible to assign a message to a variable? - smalltalk

I'm studying different kinds of programming languages to see how they differ and their advantages/disadvantages.
I'm currently particularly interested in languages that use messages for method calls; and I was wondering if it's possible to somehow assign a message to a variable in Squeak/Pharo/Smalltalk/etc.
So let's say both class A and B have the message foo:; how can I then do something like this:
|msg|
msg := foo: 12.
a msg.
b msg.
Where a and b are instances of A and B respectively

Pharo has Message class. So you can create it as
Message selector: #foo: argument: 12
But currently Message is not used for execution purposes.
What you are looking for are perform: messages.
So you can do what you need like this:
| selector arg |
selector := #foo:.
arg := 12.
a perform: selector with: arg.
b perform: selector with: arg
"for messages of other `shape`"
a perform: selector.
a perform: selector with: arg with: arg. "up to 3 args"
a perform: selector withArguments: { arg . arg }
As for fancy syntax
msg := foo: 12.
does not make any sense according to Smalltalk. But what you can do is to define a class like GenericMessage with 2 instance variables: selector and arguments. Then you redefine doesNotUnderstand: on the class side like this:
GenericMessage class >> doesNotUnderstand: aMessage
^ self new
selector: aMessage selector;
arguments: aMessage arguments;
yourself
Then you also define a method for Object:
Object>>#performMessage: aGenericMessage
^ self
perform: aGenericMessage selector
withArguments: aGenericMessage arguments
Then your code will look like this:
|msg|
msg := GenericMessage foo: 12.
a performMessage: msg.
b performMessage: msg.

Depending whether you want just to send a message by it's name or store functionality for later use, you have different options. In the latter case you can use blocks which are Smalltalk's version of closures. You define a block as:
block = [ :arg | arg foo: 12 ]
this means that whenever you evaluate an arg with the block foo: 12 will be sent to the arg.
Your code will look like this then:
|block|
block := [ :arg | arg foo: 12 ].
block value: a.
block value: b
P.S. I bet you have the same thing in Objective-C and they are also called blocks

Related

Get method called-as str in the callee

I would like to introspect the tail end of a method call from the callee side.
Right now I am doing this explicitly...
# caller side
s.pd: '.shape';
s.pd: '.to_json("test.json")';
s.pd: '.iloc[2] = 23';
# callee side
method pd( Str $call ) {
given $call {
when /shape/ { ... }
when /to_json/ { ... }
#etc
}
}
BUT, I would like to do this by way of a 'slang method call', something like this made up code...
# caller side
s.pd.shape;
s.pd.to_json("test.json");
s.pd.iloc[2] = 23;
^ ^^ ^^^^^^^^^^^^^^^^^^^$
| | |
| | L a q// str I can put into eg. a custom Grammar
| |
| L general method name
|
L invocant
#callee side
method pd( ?? ) {
my $called-as-str = self.^was-called-as;
say $called-as-str; #'pd.to_json("test.json")'
...
}
(HOW) can this be done in raku?
Since there are 422 calls to handle, of many arity patterns, answers that require the declaration of 422 methods and signatures in the called Class will be less attractive.
Per #jonathans comment, the raku docs state:
A method with the special name FALLBACK will be called when other
means to resolve the name produce no result. The first argument holds
the name and all following arguments are forwarded from the original
call. Multi methods and sub-signatures are supported.
class Magic {
method FALLBACK ($name, |c(Int, Str)) {
put "$name called with parameters {c.raku}" }
};
Magic.new.simsalabim(42, "answer");
# OUTPUT: «simsalabim called with parameters ⌈\(42, "answer")⌋␤»
So my code example would read:
# callee side
class Pd-Stub {
method FALLBACK ($name, Str $arg-str ) {
say "$name called with args: <<$arg-str>>"
}
}
class Series {
has Pd-Stub $.pd
}
my \s = Series.new;
# caller side
s.pd.shape; #shape called with args: <<>>
s.pd.to_json("test.json"); #to_json called with args: <<test.json>>
s.pd.iloc[2] = 23; #iloc called with args: <<>>
#iloc needs to use AT-POS and Proxy to handle subscript and assignment

Creating a key value message in Smalltalk/Pharo that take blocks as argument

I have a scenario where a class holds two instance variables that are mutually exclusive. That is only one can be instantiated at a time. To be precise, I have a Promise class (trying to add promises to Pharo) and it holds promiseError and promiseValue instance variables. I then want to implement the method "then: catch:".
This method should work as follows:
promiseObject := [10/0] promiseValue.
promiseObject then : [ : result | Transcript crShow : result ]
catch : [ : failure | Transcript crShow : failure ] .
I got an idea on how to implement methods that take a block as an argument from method that accepts a block and the block accepts an argument.
My attempt below will obviously not work but I have no idea on how to make it work.
then:aBlock catch: anotherBlock
|segment|
promiseValue ifNil: [ segment := promiseError ] ifNotNil: [ segment := promiseValue ].
promiseValue ifNil: [ segment := promiseValue ] ifNotNil: [ segment := promiseError ].
aBlock value:segment.
anotherBlock value: segment
This should work analogously to a try-catch block.
Have you tried something like this?
then: aBlock catch: anotherBlock
promiseError notNil ifTrue: [^anotherBlock value: promiseError].
^aBlock value: promiseValue
Note that the code does not rely on promiseValue being nil or not because nil could be a valid answer of the promise. However, if there is some promiseError, we know the promise failed, and succeeded otherwise.
Of course, here I'm assuming that this message will get sent once the promise has been successfully or unsuccessfully finished. If this is not the case, then the code should be waiting on the promise semaphore.

What does the second colon in "List:D:" mean in Perl 6?

In the doc.perl6.org, i've seen many methods like this:
method sum(List:D: --> Numeric:D)
I konw List:D is a type of List that is defined, but what does the colon after the D mean (i.e. the second one in List:D:)?
I found some explain in S12-objects:
=head2 Invocants
Declaration of the invocant is optional. You may always access the
current invocant using the keyword self.
...
To mark an explicit invocant, just put a colon after it:
method doit ($x: $a, $b, $c) { ... }
but I don't understand, it's somewhat strange at first glance.
By default methods have an invocant of self
So both of these would be equivalent:
method foo ( $a ){…}
method foo ( \self: $a ){…} # generates warning
So expanding the first example out to what it is sort-of short for
method sum( List:D \self: --> Numeric:D ){…} # generates warning
Basically you write it that way if you want to specify the type of the invocant (first argument) to a method, but just want to use self rather than specify a new variable.
The reason it uses the : to split up the invocant from the rest of the parameters is to simplify the common case where you don't specify the invocant, or type of the invocant.
When you define a sub with a basic type constraint like this:
sub testB (Str $b) { say $b; }
then you can call it with an actual instance of the type in question as well as with the type object itself:
> testB("woo")
woo
> testB(Str)
(Str)
The :D is an additional type constraint, so you can only pass a "defined" instance:
sub testC (Str:D $c) { say $c; }
> testB(Str)
(Str)
> testC("hoo")
hoo
> testC(Str)
Parameter '$c' of routine 'testC' must be an object instance of type 'Str', not a type object of type 'Str'. Did you forget a '.new'?
in sub testC at <unknown file> line 1
in block <unit> at <unknown file> line 1
More details can be found here

Using xml-rs xml::reader::Error to return a custom error

I'm using xml-rs to do some xml parsing and there are some special cases where I want to issue a custom xml::reader::Error. This error struct is implemented as:
pub struct Error {
pos: TextPosition,
kind: ErrorKind,
}
pos and kind attributes are private, so I cannot instantiate a Error manually, and there is no new() method or something like that.
What I have is this From implementation:
impl<'a, P, M> From<(&'a P, M)> for Error where P: Position, M: Into<Cow<'static, str>> {
fn from(orig: (&'a P, M)) -> Self {
Error{
pos: orig.0.position(),
kind: ErrorKind::Syntax(orig.1.into())
}
}
}
I can use this to instantiate a custom error, I need a P which implies a type which implements xml::common::Position trait and a M which is a Into<Cow<'static, str>>.
For, P, I think I can use xml::reader::EventReader since it implements xml::common::Position. But I'm not sure how to obtain Into<Cow<'static, str>>.
I tried doing something like this:
(event_reader, "custom error").into()
But it doesn't work, I think because "custom error" cannot be converted to Into<Cow<'static, str>> or something like that.
The error message I receive is this one:
error[E0277]: the trait bound `xml::reader::Error: std::convert::From<(xml::EventReader<std::io::BufReader<&[u8]>>, &str)>` is not satisfied
--> src/main.rs:234:78
|
234 | _ => return Err((event_reader, "custom error").into()),
| ^^^^ the trait `std::convert::From<(xml::EventReader<std::io::BufReader<&[u8]>>, &str)>` is not implemented for `xml::reader::Error`
|
= help: the following implementations were found:
<xml::reader::Error as std::convert::From<(&'a P, M)>>
<xml::reader::Error as std::convert::From<xml::util::CharReadError>>
<xml::reader::Error as std::convert::From<std::io::Error>>
= note: required because of the requirements on the impl of `std::convert::Into<xml::reader::Error>` for `(xml::EventReader<std::io::BufReader<&[u8]>>, &str)`
As can be seen, from the error, Rust is unable to convert my (event_reader, "custom error") tuple to the available xml::reader::Error as std::convert::From<(&'a P, M)> implementation.
That's what I want to know why and how to fix.
The problem was that I was not adding the & to the event_reader. Adding it fixed the issue and now the From implementation is called and I can instantiate an xml::reader::Error:
(&event_reader, "custom error").into()

Blocks and ifTrue statement

I want to create a method that gets a block as an argument, and the block gets a parameter as well.
If the block returns true it should do something ( for example return 1), and if it returns false it should do something else.
this is what I did.. but I am getting syntax error on the ifTrue...
is this the way I should get as a parameter a block that receives an argument?
Mymethod: Block
Block value: 'argument'
ifTrue: [ ^1].
ifFalse: [^2].
and the call to the method :
object := myClass new.
argument :=1
boolValue := object Mymethod : [:argument | argument ==1 ]
the way you wrote it means that #value:ifTrue: message to the Block, and then you are sending #ifFalse: message to nothing (which is not possible at all. If you want to do it in one line, you should use parenthesis:
(Block value: 'argument')
ifTrue: [ ^1]
ifFalse: [^2]
Also in smalltalk it's a convention to name variables with uncapitalized, like block or aBlock