Haskell: Are type variables in “where” clauses in the same namespace with their parents?

后端 未结 2 1094
北恋
北恋 2021-01-13 05:13

In the following snippet (I have abstracted all other trivial parts)

data T s = T (s -> s)

foo :: T s -> s -> s
foo (T f) x = bar x where
    bar :         


        
相关标签:
2条回答
  • 2021-01-13 05:50

    Are type variables in “where” clauses in the same namespace with their parents?

    No*. This gets a little bit easier if you think of foo :: s -> s in terms of foo :: forall s. s -> s. After all, a type variable indicates that the function works for any type s. Let's add explicit quantifications to your code:

    {-# LANGUAGE ExplicitForAll #-}
    
    data T s = T (s -> s)
    
    foo :: forall s. T s -> s -> s
    foo (T f) x = bar x where
        bar :: forall s. s -> s
        bar a = f a
    

    As you can see, there are two forall s. there. But the one in bar is wrong. After all, you cannot choose any s there, but the one already used in s. This can be done by enabling ScopedTypeVariables:

    {-# LANGUAGE ScopedTypeVariables #-}
    
    data T s = T (s -> s)
    
    --     vvvvvvvv  explicit here
    foo :: forall s. T s -> s -> s
    foo (T f) x = bar x where
        --     vvvvvv  same as above here
        bar :: s -> s
        bar a = f a
    

    However, there are some tricks to get rid of ScopedTypeVariables. For example the following in this case:

    data T s = T (s -> s)
    
    foo :: T s -> s -> s
    foo (T f) x = (bar `asTypeOf` idType x) x where
    
        bar a = f a
    
        idType :: a -> a -> a
        idType a _ = a
    
    -- For completion, definition and type of 'asTypeOf'
    -- asTypeOf :: a -> a -> a
    -- asTypeOf x _ = x
    

    For any x :: s the term idType x has type s -> s, and asTypeOf enforces both to have the same type.

    Depending on your actual code, something like this might be more or less feasible.


    * Well, in this case, since it's possible to use ScopedTypeVariables, see later part of the answer.

    0 讨论(0)
  • 2021-01-13 05:52

    ScopedTypeVariables are indeed the solution, but there's an extra requirement to use them: You must put explicit foralls in the type signatures declaring the variables you want to scope, like this:

    foo :: forall s. T s -> s -> s
    

    This is so that the meaning of signatures in code doesn't depend on whether the extension is enabled or not.

    0 讨论(0)
提交回复
热议问题