Consider the following code where we create a pipeline. The pipeline is made of Step
s. Step
is an interface with two struct that implement it; firstStep
and followingStep
. At each step a function is applied on the input
to infer the output
. The followingStep
contains an input
and an output
but firstStep
only contains an output
. The code uses generics, where types are inferred from the previous step and from the function that will, at each step, process the from
into the to
.
package main
import (
"fmt"
"strconv"
)
type Step[Input any, Output any] interface {
getOutput() Output
}
type FirstStep[Output any] struct {
output Output
}
var _ Step[int, int] = &FirstStep[int]{}
func (t *FirstStep[Output]) getOutput() Output {
return t.output
}
type FollowingStep[Input any, Output any] struct {
input Input
output Output
}
var _ Step[int, float64] = &FollowingStep[int, float64]{}
func (s *FollowingStep[Input, Output]) getOutput() Output {
return s.output
}
func First[Output any](output Output) Step[Output, Output] {
return &FirstStep[Output]{output: output}
}
func Then[Input any, Output any](
previousStep Step[Input, Input],
process func(Input) Output,
) Step[Input, Output] {
input := previousStep.getOutput()
return &FollowingStep[Input, Output]{
input: input,
output: process(input),
}
}
func main() {
first := First(3)
second := Then(first, func(i int) float64 { return float64(i) * 1.5 })
third := Then(second, func(f float64) float64 { return f / 2.0 })
fourth := Then(third, func(f float64) string { return strconv.FormatFloat(f, 'f', -1, 64) })
fmt.Printf("%v (%T)", fourth.getOutput(), fourth.getOutput()) // outputs 2.25 (string)
}
It works (demo). However, the function definition of Then
disturbs me
func Then[Input any, Output any](
previousStep Step[Input, Input],
process func(Input) Output,
) Step[Input, Output]
previousStep
is not of type Step[Input, Input]
but of type Step[PreviousInput, Input]
(the Input
would equal PreviousOutput
). It is for example strange that the first call to Then
does not yield a compile time error as we feed a Step[int, float64]
object in a function that expects Step[Input, Input]
(twice the same type).
Changing this function definition to
func Then[PreviousInput any, Input any, Output any](
previousStep Step[PreviousInput, Input],
process func(Input) Output,
) Step[Input, Output]
But if I do this “fix”, I get the compile time error cannot infer PreviousInput
when calling Then
(demo). To fix it, one could just specify any type when calling then (e.g Then[struct{bool}](/* args */)
; demo) highlighting this PreviousInput
type serves no purpose.
Can you help me understand what is going on?
- Is the first version really “correct”?
- Why doesn’t
Step[From, From]
yield to a compile-time error when we give aStep[int, float64]
as argument? - Is it safe / future proof to go with
Step[From, From]
?