Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a monoidal constraint implied by this function, even though only SemiGroup is listed as a constraint?

Tags:

haskell

In the following stack script, I can't apply my strangeHeadMay function to a NonEmpty list, but it works fine on a regular list.

#!/usr/bin/env stack
{- stack script --nix --resolver lts-13.6 -}

{-# LANGUAGE ScopedTypeVariables  #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-} -- hide some scary types

import           Control.Arrow                    ((&&&))
import           Control.Monad                    (join)
import           Data.Foldable                    (toList)
import           Data.Function                    ((&))
import           Data.Functor                     ((<&>))
import           Data.List.NonEmpty               (NonEmpty(..))
import qualified Data.List.NonEmpty               as LNE
import           Data.Maybe                       (catMaybes)
import           Data.Tuple.Sequence              (sequenceT)


strangeHeadMay :: forall a t. (Semigroup (t a), _) => t a -> Maybe a
strangeHeadMay xs =
  let xsWrapped :: t (t a) = pure <$> xs -- <- Contrived example
      -- This is the functionality I actually need:
      xsMay :: Maybe (t a) = xsWrapped & (headMay &&& tailMay) & sequenceT
         <&> (\(h, t) -> foldr mappend h t)
   in do
    xs' <- xsMay
    xs' & toList & headMay

main :: IO ()
main = do
  let nexs :: NonEmpty (Maybe Double) = Nothing :| [Just (2.0)]
  let xs = LNE.toList nexs
  let xsHead = strangeHeadMay xs
  let nexsHead = strangeHeadMay nexs
  pure ()


headMay :: Foldable f => f a -> Maybe a
headMay = foldr (const . Just) Nothing

tailMay :: Foldable f => f a -> Maybe (f a)
tailMay = foldr (seq . Just) Nothing

The error is

/home/brandon/workspace/foldNEmaybes.hs:34:18: error:
    • No instance for (Monoid (NonEmpty (Maybe Double)))
        arising from a use of ‘strangeHeadMay’
    • In the expression: strangeHeadMay nexs
      In an equation for ‘nexsHead’: nexsHead = strangeHeadMay nexs
      In the expression:
        do let nexs :: NonEmpty (Maybe Double) = Nothing :| ...
           let xs = LNE.toList nexs
           let xsHead = strangeHeadMay xs
           let nexsHead = strangeHeadMay nexs
           ....
   |
34 |   let nexsHead = strangeHeadMay nexs
   |                  ^^^^^^^^^^^^^^^^^^^

Also, I must admit I'm not totally clear on why I need the partially typed signature, but changing (Semigroup (t a), _) to (Semigroup (t a)) makes bad things happen.

like image 936
bbarker Avatar asked Nov 20 '19 21:11

bbarker


1 Answers

Even though <> and mappend do the same thing, you can only use the latter if you have a Monoid constraint, so one got added to your _ in the type signature. To fix it, use foldr (<>) instead of foldr mappend.

And you don't need the hole in the type signature. You can write out what goes in it and it'll still work: Applicative t and Foldable t.

like image 93
Joseph Sible-Reinstate Monica Avatar answered Oct 13 '22 12:10

Joseph Sible-Reinstate Monica