emulating method type parameters in generic go [duplicate] - go

This question already has answers here:
How can I emulate `fmap` in Go?
(2 answers)
Closed 8 months ago.
After watching Philip Wadler's talk on featherweight go I was really excited about the newest go generics draft. But now with a version of the new generics draft available for us to play with it seems some of the things from featherweight go are no longer possible. In the both the talk, and the paper he introduces a functor like interface called List. The approach from the paper doesn't quite work.
type Any interface {}
type Function(type a Any, b Any) interface {
Apply(x a) b
}
type Functor interface {
Map(f Function) Functor
}
fails with the error: cannot use generic type Function(type a, b) without instantiation
and if you try to add type parameters to the method, and use a normal function you get: methods cannot have type parameters
I'm wonder if anyone has found an approach to making functors work with the current version of the draft.

You haven't carried the generic types through; in your example code, Functor is treating Function as if it were not generic. The correct code (which compiles, see here) would be:
type Function(type a Any, b Any) interface {
Apply(x a) b
}
type Functor(type a Any, b Any) interface {
Map(f Function(a,b)) Functor(a,b)
}

Related

How to create abstract structure implementations [duplicate]

This question already has answers here:
Implementing an interface with a wider method signature
(3 answers)
How to implement interface method with return type is an interface in Golang
(1 answer)
Closed 4 months ago.
The community reviewed whether to reopen this question 4 months ago and left it closed:
Original close reason(s) were not resolved
I've got a slice of steps []*Steps where each step has a type and a custom payload depending on it:
type Step struct {
ID string
Name string
Type StepType // Enum: StepA | StepB
Action StepAction // interface implemented by either ActionStepA | ActionStepB
// Other common fields
}
// StepAction is an interface implemented by either ActionStepA | ActionStepB
type StepAction interface {}
type ActionStepA struct {FieldA string} // Fields specific to step A
type ActionStepB struct {FieldB string} // Fields specific to step B
Depending on the Type, the Action will be a different struct: either ActionStepA or ActionStepB which just hold their own step-specific fields.
To Execute these steps, I have a StepExecutor interface:
type StepExecutor interface {
Execute(StepAction) error
}
Ideally, I'd like to have a StepExecutor implementation per StepType which contains the business logic and knows how to execute its type. E.g. have StepAExecutor() which executes ActionStepA and StepBExecutor which executes ActionStepB.
How can I achieve something like this in GOlang? I guess what I need is a concept similar to Abstract Classes?
for _, step := range steps {
executor := getExecutorForStep(step) // returns a StepExecutor type
executor.Execute(step.Action)
}
// ERROR HERE:
fun getExecutorForStep(step *Step) StepExecutor {
switch step.Type {
case StepA: return &StepAExecutor{}
case StepB: return &StepBExecutor{}
}
}
I tried the above but I'm getting errors as the StepExecutor "implementations" are not actually implementations as they have mismatched method signatures:
StepExecutor expects StepAction for the Execute method
StepAExecutor expects ActionStepA for the Execute method
StepBExecutor expects ActionStepB for the Execute method
If go doesn't allow for such a case, I'm happy to redo the architecture with a proper more common one. Let me know if you have any suggestions. I understand that in golang, methods signatures must match literally - I'm just unsure how I can properly structure this problem. I might be in need of a re-architecture.

Is there a graceful approach to deserializing Go interface types? [duplicate]

This question already has answers here:
Can I unmarshal JSON into implementers of an Interface?
(2 answers)
Closed 11 months ago.
The community reviewed whether to reopen this question 11 months ago and left it closed:
Original close reason(s) were not resolved
Given the following:
type Foo struct {
Td ThingDoer
// ... other stuff
}
type ThingDoer interface {
doThing()
}
type doerA struct {
AGuts string
}
func (a doerA) doThing() {}
type doerB struct {
BGuts string
}
func (b doerB) doThing() {}
is there a preferred serialization / deserialization strategy for Foo?
Attaching, eg, a MarshalJSON function onto doerA and doerB satisfies the serialization, but then Foo.UnmarshalJSON is effectively stuck: it can't know in advance whether the supplied JSON is of doerA or doerB type.
Edit: The linked "similar" question addresses the specific non-solution example outlined in this question. This question is asking about the existence of a graceful solution.
Imagine you have an structure Foo with one or more fields using interface types.
Lets say you have an interface Bar with two possible structures: Baz and Bam.
You can define auxiliary type (FooConf), without any interface. Only concrete types.
This structure may have a method Build() Foo that will choose the right type on each case.
To be possible define what is the concrete type you can define a signature. For instance an extra field “type” (baz or bam).
You just need to be sure about each type can marshal/unmarshal with consistency.

What is the difference between method call syntax `foo.method()` and UFCS `Foo::method(&foo)`?

Is there any difference in Rust between calling a method on a value, like this:
struct A { e: u32 }
impl A {
fn show(&self) {
println!("{}", self.e)
}
}
fn main() {
A { e: 0 }.show();
}
...and calling it on the type, like this:
fn main() {
A::show(&A { e: 0 })
}
Summary: The most important difference is that the universal function call syntax (UFCS) is more explicit than the method call syntax.
With UFCS there is basically no ambiguity what function you want to call (there is still a longer form of the UFCS for trait methods, but let's ignore that for now). The method call syntax, on the other hand, requires more work in the compiler to figure out which method to call and how to call it. This manifests in mostly two things:
Method resolution: figure out if the method is inherent (bound to the type, not a trait) or a trait method. And in the latter case, also figure out which trait it belongs to.
Figure out the correct receiver type (self) and potentially use type coercions to make the call work.
Receiver type coercions
Let's take a look at this example to understand the type coercions to the receiver type:
struct Foo;
impl Foo {
fn on_ref(&self) {}
fn on_mut_ref(&mut self) {}
fn on_value(self) {}
}
fn main() {
let reference = &Foo; // type `&Foo`
let mut_ref = &mut Foo; // type `&mut Foo`
let mut value = Foo; // type `Foo`
// ...
}
So we have three methods that take Foo, &Foo and &mut Foo receiver and we have three variables with those types. Let's try out all 9 combinations with each, method call syntax and UFCS.
UFCS
Foo::on_ref(reference);
//Foo::on_mut_ref(reference); error: mismatched types
//Foo::on_value(reference); error: mismatched types
//Foo::on_ref(mut_ref); error: mismatched types
Foo::on_mut_ref(mut_ref);
//Foo::on_value(mut_ref); error: mismatched types
//Foo::on_ref(value); error: mismatched types
//Foo::on_mut_ref(value); error: mismatched types
Foo::on_value(value);
As we can see, only the calls succeed where the types are correct. To make the other calls work we would have to manually add & or &mut or * in front of the argument. That's the standard behavior for all function arguments.
Method call syntax
reference.on_ref();
//reference.on_mut_ref(); error: cannot borrow `*reference` as mutable
//reference.on_value(); error: cannot move out of `*reference`
mut_ref.on_ref();
mut_ref.on_mut_ref();
//mut_ref.on_value(); error: cannot move out of `*mut_ref`
value.on_ref();
value.on_mut_ref();
value.on_value();
Only three of the method calls lead to an error while the others succeed. Here, the compiler automatically inserts deref (dereferencing) or autoref (adding a reference) coercions to make the call work. Also note that the three errors are not "type mismatch" errors: the compiler already tried to adjust the type correctly, but this lead to other errors.
There are some additional coercions:
Unsize coercions, described by the Unsize trait. Allows you to call slice methods on arrays and to coerce types into trait objects of traits they implement.
Advanced deref coercions via the Deref trait. This allows you to call slice methods on Vec, for example.
Method resolution: figuring out what method to call
When writing lhs.method_name(), then the method method_name could be an inherent method of the type of lhs or it could belong to a trait that's in scope (imported). The compiler has to figure out which one to call and has a number of rules for this. When getting into the details, these rules are actually really complex and can lead to some surprising behavior. Luckily, most programmers will never have to deal with that and it "just works" most of the time.
To give a coarse overview how it works, the compiler tries the following things in order, using the first method that is found.
Is there an inherent method with the name method_name where the receiver type fits exactly (does not need coercions)?
Is there a trait method with the name method_name where the receiver type fits exactly (does not need coercions)?
Is there an inherent method with the name method_name? (type coercions will be performed)
Is there a trait method with the name method_name? (type coercions will be performed)
(Again, note that this is still a simplification. Different type of coercions are preferred over others, for example.)
This shows one rule that most programmers know: inherent methods have a higher priority than trait methods. But a bit unknown is the fact that whether or not the receiver type fits perfectly is a more important factor. There is a quiz that nicely demonstrates this: Rust Quiz #23. More details on the exact method resolution algorithm can be found in this StackOverflow answer.
This set of rules can actually make a bunch of changes to an API to be breaking changes. We currently have to deal with that in the attempt to add an IntoIterator impl for arrays.
Another – minor and probably very obvious – difference is that for the method call syntax, the type name does not have to be imported.
Apart from that it's worth pointing out what is not different about the two syntaxes:
Runtime behavior: no difference whatsoever.
Performance: the method call syntax is "converted" (desugared) into basically the UFCS pretty early inside the compiler, meaning that there aren't any performance differences either.

Why is it impossible to instantiate a data structure due to "overflow while adding drop-check rules"? [duplicate]

This question already has an answer here:
"overflow while adding drop-check rules" while implementing a fingertree
(1 answer)
Closed 4 years ago.
Here is a data structure I can write down and which is accepted by the Rust compiler:
pub struct Pair<S, T>(S, T);
pub enum List<T> {
Nil,
Cons(T, Box<List<Pair<i32, T>>>),
}
However, I cannot write
let x: List<i32> = List::Nil;
playground
as Rust will complain about an "overflow while adding drop-check rules".
Why shouldn't it be possible to instantiate List::Nil?
It should be noted that the following works:
pub struct Pair<S, T>(S, T);
pub enum List<T> {
Nil,
Cons(T, Box<List<T>>),
}
fn main() {
let x: List<i32> = List::Nil;
}
playground
When the type hasn't been instantiated, the compiler is mostly worried about the size of the type being constant and known at compile-time so it can be placed on the stack. The Rust compiler will complain if the type is infinite, and quite often a Box will fix that by creating a level of indirection to a child node, which is also of known size because it boxes its own child too.
This won't work for your type though.
When you instantiate List<T>, the type of the second argument of the Cons variant is:
Box<List<Pair<i32, T>>>
Notice that the inner List has a type argument Pair<i32, T>, not T.
That inner list also has a Cons, whose second argument has type:
Box<List<Pair<i32, Pair<i32, T>>>>
Which has a Cons, whose second argument has type:
Box<List<Pair<i32, Pair<i32, Pair<i32, T>>>>>
And so on.
Now this doesn't exactly explain why you can't use this type. The size of the type will increase linearly with how deeply it is within the List structure. When the list is short (or empty) it doesn't reference any complex types.
Based on the error text, the reason for the overflow is related to drop-checking. The compiler is checking that the type is dropped correctly, and if it comes across another type in the process it will check if that type is dropped correctly too. The problem is that each successive Cons contains a completely new type, which gets bigger the deeper you go, and the compiler has to check if each of these will be dropped correctly. The process will never end.

Cannot use Rayon's `.par_iter()`

I have a struct which implements Iterator and it works fine as an iterator. It produces values, and using .map(), I download each item from a local HTTP server and save the results. I now want to parallelize this operation, and Rayon looks friendly.
I am getting a compiler error when trying to follow the example in the documentation.
This is the code that works sequentially. generate_values returns the struct which implements Iterator. dl downloads the values and saves them (i.e. it has side effects). Since iterators are lazy in Rust, I have put a .count() at the end so that it will actually run it.
generate_values(14).map(|x| { dl(x, &path, &upstream_url); }).count();
Following the Rayon example I tried this:
generate_values(14).par_iter().map(|x| { dl(x, &path, &upstream_url); }).count();
and got the following error:
src/main.rs:69:27: 69:37 error: no method named `par_iter` found for type `MyIterator` in the current scope
Interestingly, when I use .iter(), which many Rust things use, I get a similar error:
src/main.rs:69:27: 69:33 error: no method named `iter` found for type `MyIterator` in the current scope
src/main.rs:69 generate_values(14).iter().map(|tile| { dl_tile(tile, &tc_path, &upstream_url); }).count();
Since I implement Iterator, I should get .iter() for free right? Is this why .par_iter() doesn't work?
Rust 1.6 and Rayon 0.3.1
$ rustc --version
rustc 1.6.0 (c30b771ad 2016-01-19)
Rayon 0.3.1 defines par_iter as:
pub trait IntoParallelRefIterator<'data> {
type Iter: ParallelIterator<Item=&'data Self::Item>;
type Item: Sync + 'data;
fn par_iter(&'data self) -> Self::Iter;
}
There is only one type that implements this trait in Rayon itself: [T]:
impl<'data, T: Sync + 'data> IntoParallelRefIterator<'data> for [T] {
type Item = T;
type Iter = SliceIter<'data, T>;
fn par_iter(&'data self) -> Self::Iter {
self.into_par_iter()
}
}
That's why Lukas Kalbertodt's answer to collect to a Vec will work; Vec dereferences to a slice.
Generally, Rayon could not assume that any iterator would be amenable to parallelization, so it cannot default to including all Iterators.
Since you have defined generate_values, you could implement the appropriate Rayon trait for it as well:
IntoParallelIterator
IntoParallelRefIterator
IntoParallelRefMutIterator
That should allow you to avoid collecting into a temporary vector.
No, the Iterator trait has nothing to do with the iter() method. Yes, this is slightly confusing.
There are a few different concepts here. An Iterator is a type that can spit out values; it only needs to implement next() and has many other methods, but none of these is iter(). Then there is IntoIterator which says that a type can be transformed into an Iterator. This trait has the into_iter() method. Now the iter() method is not really related to any of those two traits. It's just a normal method of many types, that often works similar to into_iter().
Now to your Rayon problem: it looks like you can't just take any normal iterator and turn it into a parallel one. However, I never used this library, so takes this with a grain of salt. To me it looks like you need to collect your iterator into a Vec to be able to use par_iter().
And just as a note: when using normal iterators, you shouldn't use map() and count(), but rather use a standard for loop.

Resources