Shaping a type without requiring a type parameter?

Hello,

I’m very new to F# and I’m exploring the idea of writing CI/CD pipelines with it.

I wrote the following types:

type Step<'a> = { Name: string; GetContext: (unit -> 'a); Run: ('a -> unit) }

type CompositeStep<'a> =
    | Step of Step<'a>
    | Sequential of string * CompositeStep<'a> list
    | Parallel of string * CompositeStep<'a> list

Here is how I expect to use them:

let myPipeline =
    Sequential("A pipeline", [
          Step(step1)
          Parallel("parallel stage", [
              Step(step2)
              Step(step3)
          ])
          Step(step4) ]
    )

Now, the problem is obvious, unless all the steps of your pipeline use the same context record type, you won’t be able to mix arbitrary steps. If you have steps with a different context type returned by GetContext, it won’t compile.

I tried to resolve this and it seems the only way this can work is if the parameter for a step is always obj, which defeat the purpose of the type system for this particular case.

Ideally, I would like the Step type to be written like so:

type Step = { Name: string; GetContext: (unit -> 'a); Run: ('a -> unit) }

Here there is a type parameter shaping the relation between the functions in the record but this is not a parameter coming from Step (if that makes sense). Alas, it doesn’t seem to be allowed.

Am I missing something? Since I’m new to F#, I expect my approach to be somewhat wrong. Is there a better way to do this?

Thanks for your help.

You don’t really have a F# problem at the moment. You have a “I am not representing what I want to represent”.

You are modeling all steps as having a single type. You are creating a pipeline where you want to be able to mix types.

You need to have each step have an input type and an output type. And you need to only allow connecting matching output types to the corresponding input type.

You would going want to explicitly represent sources and sinks. Sources only have “an output type”. Sinks only have “an input type”. Then you want to have a single source per pipeline and a single sink and have steps you can connect in between.

From there, you need to add the ability to split and connect in the pipeline so you can do your “Parallel” type that you want.