Search code examples
haskellpolyvariadic

Result type of a polyvariadic function in haskell


While studying polyvariadic functions in Haskell I stumbled across the following SO questions:

How to create a polyvariadic haskell function?

Haskell, polyvariadic function and type inference

and thought I will give it a try by implementing a function which takes a variable number of strings and concatenates/merges them into a single string:

{-# LANGUAGE FlexibleInstances #-}

class MergeStrings r where
    merge :: String -> r
instance MergeStrings String where
    merge = id
instance (MergeStrings r) => MergeStrings (String -> r) where
    merge acc = merge . (acc ++)

This works so far if I call merge with at least one string argument and if I provide the final type.

foo :: String
foo = merge "a" "b" "c"

Omitting the final type results in an error, i.e., compiling the following

bar = merge "a" "b" "c"

results in

test.hs:12:7: error:
    • Ambiguous type variable ‘t0’ arising from a use of ‘merge’
      prevents the constraint ‘(MergeStrings t0)’ from being solved.
      Relevant bindings include bar :: t0 (bound at test.hs:12:1)
      Probable fix: use a type annotation to specify what ‘t0’ should be.
      These potential instances exist:
        instance MergeStrings r => MergeStrings (String -> r)
          -- Defined at test.hs:6:10
        instance MergeStrings String -- Defined at test.hs:4:10
    • In the expression: merge "a" "b" "c"
      In an equation for ‘bar’: bar = merge "a" "b" "c"
   |
12 | bar = merge "a" "b" "c"
   |

The error message makes perfect sense since I could easily come up with, for example

bar :: String -> String
bar = merge "a" "b" "c"

baz = bar "d"

rendering bar not into a single string but into a function which takes and returns one string.

Is there a way to tell Haskell that the result type must be of type String? For example, Text.Printf.printf "hello world" evaluates to type String without explicitly defining.


Solution

  • printf works without type annotation because of type defaulting in GHCi. The same mechanism that allows you to eval show $ 1 + 2 without specifying concrete types.

    GHCi tries to evaluate expressions of type IO a, so you just need to add appropriate instance for MergeStrings:

    instance (a ~ ()) => MergeStrings (IO a) where
        merge = putStrLn