How to fix type errors, including “Can't quant

2020-04-30 18:05发布

问题:

I'll just say I'm not even sure if this is possible; it is by far the most generic thing I've tried doing in Haskell. I'm trying to make a more generic version of the applyFunc found in https://stackoverflow.com/a/58890226/3096687:

newtype SummaryFun t a = SummaryFun (t a -> a)

applyRecFun :: (Traversable t, _) => Record (SummaryFun t) _ -> t r -> r
applyRecFun func recs =
  let recs_NP :: t (NP I _) -- a list of n-ary products. I is an identity functor
      recs_NP = toNP . toRecord <$> recs
      listrecs_NP :: t (NP t _) -- turn every component into a singleton list
      listrecs_NP = liftA_NP (\(I x) -> pure x) <$> recs_NP
      listrec_NP :: NP t _ -- a single n-ary product where each component is a list
      listrec_NP = mconcat listrecs_NP
      func_NP :: NP (SummaryFun t) _ -- turn the function record into a n-ary prod
      func_NP = toNP func
      resultRec_NP_I :: NP I _ -- apply the functions to each list component
      resultRec_NP_I = liftA2_NP (\(SummaryFun f) vs -> I (f vs)) func_NP listrec_NP
   in fromRecord . fromNP $ resultRec_NP_I -- go back to the nominal record Rec

The first issue being run into is:

• Can't quantify over ‘t’                  
    bound by the partial type signature: recs_NP :: t (NP I _)

And similar errors occur elsewhere.

回答1:

I can't be sure this will fix everything, because it's very hard to reproduce your problem: it requires a lot of packages and imports that I just don't have the time to set up (hint: in the future, try to reduce your problems to minimal reproducible examples before posting). But I'm going to post this anyway, because I can see at least one problem, and it seems to be related to the error message.


The problem is that t on the first line is not the same as t on the third line, and they are both different from the t on the fifth line. And so on, for all type signatures involving t.

By default, in Haskell2010 every type variable's scope is only the type signature in which it's introduced. If you use the same letter in another type signature, it will signify a completely separate type, despite looking identical to the human eye.

To specify that you actually mean t to be the same everywhere, you have to use forall in the top type signature:

applyRecFun :: forall t. (Traversable t, _) => Record (SummaryFun t) _ -> t r -> r

Enabled by the ScopedTypeVariables extension, the forall keyword creates an explicit scope for the type variable t. Scopes come in different flavors, but when opened in a function's type signature, the scope's extent is the whole body of that function.

I'm not sure this will solve everything for you, but at least you should be getting different errors now.



回答2:

Building on Fyodor's answer, I have a compiling (but as yet untested) code. I had to change a few assumptions in the type signature, such as returning the result wrapped in Maybe, and assuming the Traversable also forms a Semigroup. I'll revise the wording of the question and this answer once I've had a chance to properly test and construct a self-contained example as suggested by Fyodor:

newtype SummaryFun t a = SummaryFun (t a -> a)

applyRecFun :: forall r t. (Traversable t, Semigroup (t r), _) =>
  Record (SummaryFun t) _ -> t r -> Maybe r
applyRecFun func recs =
  let recsNP :: t (NP I _) -- a traversable of n-ary products. I is an identity functor
      recsNP = toNP . toRecord <$> recs
      listrecsNP :: t (NP t _) -- turn every component into a singleton traversable
      listrecsNP = liftA_NP (\(I x) -> pure x) <$> recsNP
      listrecNPmay :: Maybe (NP t _) -- a single n-ary product where each component is a traversable
      listrecNPmay = listrecsNP & (headMay &&& tailMay) & sequenceT
        <&> (\(h, t) -> foldr mappend h t)
      funcNP :: NP (SummaryFun t) _ -- turn the function record into a n-ary prod
      funcNP = toNP func
      toRec_NP_I :: NP t _ -> NP I _ -- apply the functions to each list component
      toRec_NP_I = liftA2_NP (\(SummaryFun f) vs -> I (f vs)) funcNP
   in do
    listrecNP <- listrecNPmay
    let resultRec_NP_I = toRec_NP_I listrecNP
    pure $ fromRecord . fromNP $ resultRec_NP_I -- go back to the nominal record Rec