Arrays with rigid variable

后端 未结 2 980
轻奢々
轻奢々 2021-01-22 17:29

Well, I was doing a problem in which function I was using had a rigid variable. I had an idea of using arrays for that problem. So I thought of using arrays with same rigid vari

相关标签:
2条回答
  • 2021-01-22 18:24

    You're having trouble because the "inner" type signatures you've written out don't mean quite what they look like they'd mean. In particular, your uses of c do not correspond with one another not the a in the top signature and they must. Haskell is complaining that these "rigidly defined" type variables, despite being variables, cannot be the same, but since they're based on a "rigid" choice of what a is... they must!

    You can make GHC (but not Haskell in general) behave the way you'd like with an extension called {-# LANGUAGE ScopedTypeVariables #-} where your code becomes

    {-# LANGUAGE ScopedTypeVariables #-}
    
    rearrange :: forall a . [Int] -> [a] -> [a]
    rearrange l la = elems (f 1 posarr)
      where
        b = length l
        listarr :: Array Int Int
        listarr = listArray (1, b) l
        arra :: Array Int a
        arra = listArray (1,b) la
        posarr :: Array Int a
        posarr listArray (1,b) la
        f i posarr
          | (b < i) = posarr
          | otherwise = f (i+1) (posarr // [(listarr!i,arra!i)])
    

    Note that all I did was add the explicit forall and changed some c variables to a. What ScopedTypeVariables let you do is introduce type variable scopes using forall where any type signatures in code that is indented below such an explicitly forall'd signature can re-use the type variable names introduced in that forall and have them correspond exactly.

    Which might make more sense under examining how Haskell interprets type signatures without the extension. In particular, there is an implicit forall before every type signature

                                      -- is actually
    foo :: [a] -> [a] -> [a]          foo :: forall a. [a] -> [a] -> [a]
    foo xs ys = it where              foo xs ys = it where
      it :: [a]                         it :: forall a. [a]
      it = xs ++ ys                     it = xs ++ ys
    

    Which forces the a variable in each of these type signatures to be different and thus this fragment of code cannot compile because it is only valid if those two as are the same. With ScopedTypeVariables we have

    foo :: forall a . [a] -> [a] -> [a]
    foo xs ys = it where
      it :: [a]
      it = xs ++ ys
    

    where the inner signature's a is scoped to mean exactly the same a as in outer signature's.

    0 讨论(0)
  • 2021-01-22 18:33

    J. Abrahamson explained how to fix the errors at hand, but note that this is way overcomplicated. You can just omit all the local type signatures1, the compiler is able to infer those by itself. (There sure enough are applications where you need local type signatures, or where it's helpful for readability; then you often need ScopedTypeVariables. But not for such a simple function, IMO.)

    Speaking of unnecessary complexity — there is no benefit whatsoever in explicitly index-iterating through an array you've just created with listArray: it's pretty much equivalent (but much unwieldier) to just recursively destructing the list. And that can be written as a fold. Or, in cases like this where you iterate through two lists "in parallel", to folding on the zip of those lists. In fact you don't need even the fold: there's a good reason that (//) accepts a list of index-value pairs rather than just a single pair – because you'll normally want to update multiple elements2 in batch.

    That simplifies your function drastically:

    rearrange l la = elems $ posarr // zip l la
      where posarr = listArray (1, length l) la
    

    1So I'm not misunderstood: I really mean only local type signatures. On the top-level, everything should have a signature except perhaps completely trivial stuff that's only used inside your module (but then it's in a way also a local signature).

    2There is more to this than just convenience: your solution is actually very inefficient, because at each modification step a copy of the entire array needs to be made, so you can safely access the intermediate results in the purely functional language. Calling // with multiple pairs right away omits this: since the intermediate steps are never exposed, GHC can do magic under the hood, do destructive updates like you could in an imperative language (or in Haskell with the ST monad). Which is much faster.

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