
On Fri, 2 Jun 2023, Zoran BoĆĄnjak wrote:
import Prelude hiding ((.), id) import Control.Category
data Flow a b where Id :: Flow a a Compose :: Flow a b -> Flow b c -> Flow a c
type variable 'b' of Compose is not visible outside Flow. Thus you cannot get later in runFlow, but you have to embed the Show constraint in the Compose constructor like so: Compose :: (Show b) => Flow a b -> Flow b c -> Flow a c But this in turn means, you can compose only Flows where the interim type 'b' is an instance of Show. To avoid this you would need an additional type parameter to Flow with a constraint kind constructor or an existentially quantified type, that holds all the constraints you need for your current application. I think it should be like so: type family FlowConstraints constr a data FlowAny type instance FlowConstraints FlowAny a = () data FlowShow type instance FlowConstraints FlowShow a = (Show a) data Flow constr a b where Id :: Flow a a Compose :: (FlowConstraints constr b) => Flow a b -> Flow b c -> Flow a c or alternatively: data family FlowConstraints constr a data FlowAny data instance FlowConstraints FlowAby a = FlowAnyConstraint data FlowShow data instance FlowConstraints FlowShow a = (Show a) => FlowShowConstraint data Flow constr a b where Id :: Flow a a Compose :: (FlowConstraints constr b) => Flow a b -> Flow b c -> Flow a c In this case you have to match on FlowAnyConstraint or FlowShowConstraint in runFlow in order to get back the required constraints. In any case, the constraints must already be available at construction with Compose. Thus you will not be able to use Compose in a Category instance.