Associated Parameter Restriction using Functional Dependency

前端 未结 3 775
走了就别回头了
走了就别回头了 2021-01-21 21:54

The function f below, for a given type \'a\', takes a parameter of type \'c\'. For different types \'a\', \'c\' is restricted in different ways. Concretely, when \'a\' is any In

3条回答
  •  春和景丽
    2021-01-21 22:22

    Here's a suggestion to solve a more general problem, not yours specifically (I need more detail yet first - I promise to check later). I'm writing it in case other people are searching for a solution to a similar problem to you, I certainly was in the past, before I discovered SO. SO is especially great when it helps you try a radically new approach.

    I used to have the work habit:

    1. Introduce a multi-parameter type class (Types hanging out all over the place, so...)
    2. Introduce functional dependencies (Should tidy it up but then I end up needing...)
    3. Add FlexibleInstances (Alarm bells start ringing. There's a reason the compiler has this off by default...)
    4. Add UndecidableInstances (GHC is telling you you're on your own, because it's not convinced it's up to the challenge you're setting it.)
    5. Everything blows up. Refactor somehow.

    Then I discovered the joys of type families (functional programming for types (hooray) - multi-parameter type classes are (a bit like) logic programming for types). My workflow changed to:

    1. Introduce a type class including an associated type, i.e. replace

      class MyProblematicClass a b | a -> b where
        thing :: a -> b
        thang :: b -> a -> b
      

      with

      class MyJustWorksClass a where
        type Thing a :: * -- Thing a is a type (*), not a type constructor (* -> *)
        thing :: a -> Thing a
        thang :: Thing a -> a -> Thing a
      
    2. Nervously add FlexibleInstances. Nothing goes wrong at all.

    3. Sometimes fix things by using constraints like (MyJustWorksClass j,j~a)=> instead of (MyJustWorksClass a)=> or (Show t,t ~ Thing a,...)=> instead of (Show (Thing a),...) => to help ghc out. (~ essentially means 'is the same type as')
    4. Nervously add FlexibleContexts. Nothing goes wrong at all.
    5. Everything works.

    The reason "Nothing goes wrong at all" is that ghc calculates the type Thing a using my type function Thang rather than trying to deduce it using a merely a bunch of assertions that there's a function there and it ought to be able to work it out.

    Give it a go! Read Fun with Type Functions before reading the manual!

提交回复
热议问题