How does minElement template containers work? - d

In the function below, if I change a to kv:
void main()
{
import std.algorithm.searching : minElement;
import std.stdio : writeln;
import std.array: byPair;
long[string] aa = [
"foo": 5,
"bar": 10,
"baz": 2000
];
writeln(aa.byPair().minElement!"a.value"().value);
}
compiler throws the following error message:
/dlang/dmd/linux/bin64/../../src/phobos/std/functional.d-mixin-215(215): Error: undefined identifier kv
/dlang/dmd/linux/bin64/../../src/phobos/std/algorithm/searching.d(1351): Error: template instance std.functional.binaryFun!("kv.value", "a", "b").binaryFun!(Tuple!(string, "key", long, "value"), Tuple!(string, "key", long, "value")) error instantiating
/dlang/dmd/linux/bin64/../../src/phobos/std/algorithm/searching.d(1314): instantiated from here: extremum!(__lambda2, "kv.value", MapResult!(__lambda2, Result), Tuple!(string, "key", long, "value"))
/dlang/dmd/linux/bin64/../../src/phobos/std/algorithm/searching.d(1398): instantiated from here: extremum!((a) => a, "kv.value", MapResult!(__lambda2, Result))
/dlang/dmd/linux/bin64/../../src/phobos/std/algorithm/searching.d(3550): instantiated from here: extremum!("kv.value", MapResult!(__lambda2, Result))
onlineapp.d(12): instantiated from here: minElement!("kv.value", MapResult!(__lambda2, Result))
But compiles fine with just "a.value" argument. What does this a mean?

minElement uses unaryFun to turn the passed string into a function. However, to do this it uses string mixins. The downside to this is the generated function doesn't have access to the context in which the string is created, and thus can't access the variables there.
As unaryFun's documentation says, the parameter name in the string must be a. This explains why kv fails.
Of course, as Adam D. Ruppe says, you should instead use the newer lambda syntax kv => kv.value - this allows you to us whatever parameter names you want, and allows access to the context, letting you do things like minElement!(kv => kv.value + aa["foo"]), which is simply impossible with the string functions.
Lastly, one of the possibly best reasons not to use the string functions is, as you've noticed, the error messages. Since the conversion from string to functions happens deep inside a stack of templates, you get a list of unrelated locations when the actual error is in your own code, while a lambda would show you exactly what's wrong in an easy-to-grok error message.

String parameters as functions appear as examples everywhere in std documentation, but when and how they work isn't documented very well. As you have noticed, std templates that take a function alias parameter can receive a string instead of an actual function.
This string is then converted to a "real" function using unaryFun or binaryFun which use mixin or some other magic. They name the parameters a and b, which you can use.
As Adam D. Ruppe has noted, you can also pass "normal" functions/delegates like minElement!(a => a.value)() or minElement!((a){ return a.value; }), of course parameter names are up to you then.

Related

Use a method on a StateNotifier Riverpod for changing a bool [duplicate]

In the context of a Flutter 2.0.5 app whose state I'd like to manage with Riverpod, I thought I can declare a StateNotifierProvider like this:
import 'package:flutter_riverpod/flutter_riverpod.dart';
final counterProvider = StateNotifierProvider<CounterStateNotifier>((ref) => CounterStateNotifier());
class CounterStateNotifier extends StateNotifier<int> {
CounterStateNotifier([int count = 0]) : super(count);
void increment() => state++;
}
But Android Studio (and later the Dart compiler as well) complains about the line where I declare the counterProvider variable:
The type 'StateNotifierProvider' is declared with 2 type parameters, but 1 type arguments were given.
Removing the <CounterStateNotifier> type parameter in StateNotifierProvider<CounterStateNotifier> removes the error. However, attempting to read the provider and call its increment method (setting () => context.read(counterProvider).increment() as the onPressed of an ElevatedButton, then pressing the button) gives the following runtime error:
'increment'
method not found
Receiver: 0
Arguments: []
Why is context.read(counterProvider) returning the int state instead of the notifier? And what is the reason behind the type parameter error mentioned in the first part of my question?
I should mention that I'm running my app on the web (with flutter run -d Chrome).
As of Riverpod 0.14.0, State is the default value exposed by StateNotifierProvider.
The syntax for declaring your StateNotifierProvider is now as follows:
final counterProvider = StateNotifierProvider<CounterStateNotifier, int>((ref) => CounterStateNotifier());
Accessing functions now requires adding .notifier (accessing the StateNotifier itself):
context.read(counterProvider.notifier).increment();
And like you've noticed, you now access the state like so:
final count = context.read(counterProvider);
More on the changes here.
You may also use dynamic to accept any type if value for the StateNotifierProvider
final modelProvider =
StateNotifierProvider.autoDispose<ModelClassName, dynamic>(
(ref) => ModelClassName());

Why doesn't crystal's type inference work on classes as expected?

Why can I define a method like that in Crystal:
def foo(bar): String
bar.to_json
end
foo({"x" => 1, "y" => 2})
but that kind of type inference doesn't work with classes:
class Foo
def initialize(bar)
#bar = bar
end
def foo: String
#bar.to_json
end
end
Foo.new({"x" => 1, "y" => 2}).foo
and it ends up with
Error: can't infer the type of instance variable '#bar' of Foo
What am I missing about Crystal's type inference and what is the workaround for this?
The equivalent class based approach is making the class a generic:
require "json"
class Foo(T)
def initialize(#bar : T)
end
def foo
#bar.to_json
end
end
puts Foo.new({"x" => 1, "y" => 2}).foo
Instance variables need their type set in one way or another because lexicographical type flow analysis is much harder and thus slower to do for them. Also classes build the base of your program so typing them as narrow as possible not only makes the compiler's job easier, it also makes them easier to use. Too open type restrictions on instance variables can lead to quite long and confusing error messages.
You can read more at the original proposal introducing the change to require type annotations on instance variables: https://github.com/crystal-lang/crystal/issues/2390

How to handle return types with multiple fields

I am calling a method "get_text" on GText.buffer detailed here http://oandrieu.nerim.net/ocaml/lablgtk/doc/GText.buffer.html
let text = textView#buffer#get_text in
However as get_text returns multiple values, when I try to use my variable "text" as a string, for example
textView2#buffer#set_text text;
I get the following error message:
Error: This expression has type
?start:GText.iter ->
?stop:GText.iter -> ?slice:bool -> ?visible:bool -> unit -> string
but an expression was expected of type string
How can I access the string being returned by the method? In general, how can I separate the multiple values returned by a method so I can access and use them individually?
I just looked up your link to lablgtk - it looks like you are missing the ():
let text = textView#buffer#get_text () in ...
The problem with this kind of error is that you are using a (curried) function where a string is required, and the message about the type error sounds kind of "long winded" and not to the point.

RSpec it block with variable name

I have a function get_type that returns a string given an int:
def get_type(integer)
types = [...]
return types[integer]
end
When testing with RSpec, I tried doing the following:
describe 'function' do
context 'on valid input'
let(:input){ 2 }
let(:type){ 'large' }
let(:result){ get_type input }
it{ expect(result).to eq(type) }
end
end
However, this gives the message:
function on valid input should eq "large"
without any mention to the input, thus sounding like the function should always return "large".
How should this message be changed to say something like:
function on valid input should eq type
or another meaningful message? I could name the it block:
it 'should have the correct type' do
expect(result).to eq(type)
end
but is there a nicer way to do this without essentially typing out the test twice?
I think the unhelpful message should be considered a smell - you're headed down a road where every test is just expect(result).to eq(expected) with a wall of let. To my mind this is overuse of let - I don't think you gain anything over
describe 'function' do
context 'on valid input' do
it{ expect(get_type(2)).to eq('large') }
end
end
Which would produce a more helpful failure message. I would keep let for when the expressions are more complex or when I can give them a better name (eg a hash of attributes called valid_attributes)

How to call function from hashmap in Scala

I'm pretty new to scala and basically I want to have a couple of functions coupled to a string in a hashmap.
However I get an error at subscribers.get(e.key)(e.EventArgs); stating Option[EventArgs => Unit] does not take parameters...
Example code:
object Monitor {
val subscribers = HashMap.empty[String, (EventArgs) => Unit ]
def trigger(e : Event){
subscribers.get(e.key)(e.EventArgs);
}
def subscribe(key: String, e: (EventArgs) => Unit) {
subscribers += key -> e;
}
}
The get method of a Map gives you an Option of the value, not the value. Thus, if the key if found in the map, you get Some(value), if not, you get None. So you need to first "unroll" that option to make sure there is actually a value of a function which you can invoke (call apply on):
def trigger(e: Event): Unit =
subscribers.get(e.key).foreach(_.apply(e.EventArgs))
or
def trigger(e: Event): Unit =
subscribers.get(e.key) match {
case Some(value) => value(e.EventArgs)
case None =>
}
There are many posts around explaining Scala's Option type. For example this one or this one.
Also note Luigi's remark about using an immutable map (the default Map) with a var instead.
Since the get method returns Option, you can use 'map' on that:
subscribers.get(e.key).map(f => f(e.EventArgs))
or even shorter:
subscribers.get(e.key) map (_(e.EventArgs))
get only takes one argument. So subscribers.get(e.key) returns an Option, and you're trying to feed (e.EventArgs) to that Option's apply method (which doesn't exist).
Also, try making the subscribers a var (or choosing a mutable collection type). At the moment you have an immutable collection and an immutable variable, so your map cannot change. A more idiomatic way to declare it would be
var subscribers = Map[String, EventArgs => Unit]()
HashMap.get() in Scala works in a bit different way, than in Java. Instead of returning value itself, get() returns Option. Option is a special type, that can have 2 values - Some(x) and None. In first case it tells "there's some value with such a key in a map". In second case it tells "nope, there's nothing (none) for this key in a map". This is done to force programmers check whether map actually has an object or not and avoid NullPointerException, which appears so frequently in Java code.
So you need something like this:
def trigger(e: Event) {
val value = subscribers.get(e.key)
value match {
case None => throw new Exception("Oops, no such subscriber...")
case Some(f) => f(e.EventArgs)
}
}
You can find more info about Option type and pattern matching in Scala here.