Circular Type Constraints

.net, f#, generics

Solution

A solution to avoid getting into the problem in the first place

Not a direct answer to your question, but it should solve your original problem:

type StateMachine<'State, 'Action> =
    interface
        abstract Apply : 'State * 'Action -> 'State
        abstract GetActions : 'State -> 'Action seq
    end

This way of approaching the problem is inspired by ML's module system

An uglier solution

If you really want to go for the two tightly coupled interfaces, you could go that way:

type IState<'Action, 'State when 'Action :> IAction<'State, 'Action> and 'State :> IState<'Action, 'State>> =
    interface
        abstract GetActions : unit -> 'Action seq
    end

and IAction<'State, 'Action when 'Action :> IAction<'State, 'Action> and 'State :> IState<'Action, 'State>> =
    interface
        abstract Apply : 'State -> 'State
    end

// Some stupid types to illustrate how to implement the interfaces
type State() =
    interface IState<Action, State> with
        member this.GetActions() = Seq.empty

and Action() =
    interface IAction<State, Action> with
        member this.Apply s = s

I hope people won't start using the second solution and make a design pattern named after me out of it :)

Problem

I have two interfaces: `IState` and `IAction`. A State has a method: GetActions - which returns a collection of IActions. A Action has a method: Apply - which acts on a State, to return a new State. IState takes a type parameter to control what sort of actions it returns with get actions, IAction takes a type parameter to control what sort of states it can act on. (By sort, i ment implementation). I want to be able to garentee that a State only return actions that can act on a state of that same type. ``` type IAction<'S when 'S:>IState> = abstract member Apply : 'S->'S and IState<'A when 'A:>IAction<'S when 'S:> typeof(this)>> = abstract member GetActions : seq<'A> ``` but obviously `typeof(this)` is not a thing. How can I have a type constraint making sure my type paramerer is of type equal to the type I am defining?

Original source