简体   繁体   中英

Haskell Maybe/Parametric Polymorphism

I have this code :

getLengthOfMissingArray :: Maybe [Maybe [Int]] -> Maybe Int 
getLengthOfMissingArray maybelist = do
  ns <- maybelist 
  getMissing ns

getMissing :: [Maybe [Int]] -> Maybe Int 
getMissing maybelist
  | any (==Nothing) maybelist = Nothing
  | any (==Just []) maybelist = Nothing
  | otherwise                 = Just (sumn mx - sumn (mn - 1) - sum l0)
  where
    l0 = map length (catMaybes maybelist)
    (mn,mx) = (minimum l0, maximum l0)
    sumn n = n * (n + 1) `quot` 2

Which is a Haskell translation of this :

https://www.codewars.com/kata/length-of-missing-array

Now it works, however I have two issues :

  • it is kind of fugly
  • it should work on any list, not just int's

I would like to have getMissing just be :

getMissing :: [[Int]] -> Int 

And then just use that function inside of the other one, but I don't know how to utilize that function on the Maybe of a Maybe .

Second, I tried simply doing :

getMissing :: Eq a => [Maybe [a]] -> Maybe Int 
getLengthOfMissingArray :: Eq a => Maybe [Maybe [a]] -> Maybe Int 

But then Haskell generates errors when I try to use QuickCheck on test cases like :

getLengthOfMissingArray (Just [ Nothing, Just [ 4, 5, 1, 1 ], Just [ 1 ],
                                Just [ 5, 6, 7, 8, 9 ] ])     `shouldBe` Nothing

That works, but this generates type errors :

getLengthOfMissingArray (Just [Just []])                      `shouldBe` Nothing

I guess without an actual a there there's an issue because if I just try to run it on a Nothing it also generates the same type error:

"Ambiguous type variable 'a0' arising from a use of 'getLengthOfMissingArray' prevents the constraint '(Eq a0)' from being solved."

sequence :: [Maybe a] -> Maybe [a] will help you adapt your [Maybe [Int]] to [[Int]] .

join :: Maybe (Maybe a) -> Maybe a might also be useful.

Both sequence and join have more general types - I wrote them as I expect you will use them here.

You will see "Ambiguous type variable" errors when you pass a polymorphic value to a function that is polymorphic in its argument. To actually run the function GHC needs to use a specific type, and it won't choose - you need to. Since you want getLengthOfMissingArray to remain polymorphic, you can add a type signature to the test case to specify how the tests should run.

Thanks to bergey (and dramforever on cw) :

getLengthOfMissingArray :: Eq a => Maybe [Maybe [a]] -> Maybe Int 
getLengthOfMissingArray maybelist = do
  list  <- maybelist 
  ns    <- sequence list 
  getMissing ns

getMissing :: Eq a => [[a]] -> Maybe Int 
getMissing list
  | any (==0) l0  = Nothing
  | otherwise     = Just $ sumn mx - sumn (mn - 1) - sum l0
  where
    l0 = map length list
    (mn,mx) = (minimum l0, maximum l0)
    sumn n = n * (n + 1) `quot` 2

And adjusting the tests :

getLengthOfMissingArray (Nothing :: Maybe [Maybe [Int]]) `shouldBe` Nothing

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM