How to define function signatures partially in Haskell?

Starting point:

fn :: [a] -> Int
fn = (2 *) . length

Let's say we only want to constrain the return value, then we could write:

fn list = (2 * length list) :: Int

How about restricting only the argument? Easy.

fn list = 2 * length (list :: [Char])

While this works, it would be preferable to have the signatures at the top collected and not scattered around the function body.

This is the closest I could come to this:

fnSig = undefined :: [Char] -> a
fn | False = fnSig
   | True  = (* 2) . length

Based on http://okmij.org/ftp/Haskell/partial-signatures.lhs via http://okmij.org/ftp/Haskell/types.html#partial-sigs

However, I'd like a cleaner solution. Something that communicates better that my intent is partial restriction. Something like this, for example:

fn :: [Char] -> a
fn = (2 *) . length

Or maybe:

fn :: [Char] -> _
fn = (2 *) . length

Is this possible?

Edit for further clarification:

@GaneshSittampalam Made an important point in a comment below. I am looking for "a half-way house between no type signature at all and having to give a precise one". So, I am not looking for a TypeClass-based answer, I just want GHC to fill in the blanks for the unspecified (or not fully restricted) types of my function.

Edit in response to @WillNess

Yes, something like this...

fn list = 2 * length list
  where
    _ = list :: [Char]

...could work, but only for arguments, and only if the function is not point-free. Is there a way to apply this technique to point-free functions or return values?

Edit in response to @Rhymoid

I got inspired, and played around with @Rhymoid's idea, and came up with this:

fn = (2 *) . length
  where
    _ = fn `asTypeOf` (undefined :: [Char] -> a)
    _ = fn `asTypeOf` (undefined :: a -> Int)
    _ = fn `asTypeOf` (undefined :: a -> b)
    _ = fn `asTypeOf` (undefined :: a)

This approach also restricts fn's type signature, and doesn't pollute any namespace.

Ordinarily we would only have one of the asTypeOf lines, I just added multiple to showcase how powerful this approach is.

It is a little more clumsy than how I would like it, but I guess it is pretty neat we can do this even without specific syntactic support from the language.

@Rhymoid, if you like it too, please add it to your answer. :)


Solution 1:

Sorry for the self-promotion, but exactly this feature is the topic of a recent paper by Ph.D. student Thomas Winant, myself, Frank Piessens and Tom Schrijvers, very recently presented by Thomas at the PADL 2014 symposium. See here for the full paper. It is a feature that is already present in some other languages, but the interaction with features like Haskell GADTs made it interesting enough to work out the details.

Thomas is working on an implementation for GHC. It has further improved since the writing of the paper, but implementing the "wildcard constraint" in GHC is technically a bit harder than we expected. We expect to be able to work further on it and contact the GHC developers to get it adopted, but whether or not this happens may depend on how much people would like to have the feature in Haskell...

Update 14-4-2015: After a lot of work by Thomas and input from SPJ and other GHC people, partial type signatures have been released in GHC 7.10. Thomas Winant wrote an introductory blog post about how you can use them.

Solution 2:

I've been looking for a way to say 'x's type unifies with T'. The solutions given by Will Ness and chi are close to what I came up with, but there is a way to do it in Haskell 98, without butchering your own function.

-- Your function, without type signature.
fn = (2 *) . length

-- The type signature, without actual definition.
fnTy :: [Char] -> a
fnTy = undefined

-- If this type checks, then the type of 'fn' can be unified 
--                                      with the type of 'fnTy'.
fn_unifies_with_type :: ()
fn_unifies_with_type = let _ = fn `asTypeOf` fnTy in ()

You could even go for just

fn = (2 *) . length
  where
    _ = fn `asTypeOf` (undefined :: [Char] -> a)

Solution 3:

You're looking for feature that many of us would like, but that Haskell doesn't have. Nor ghc. You want a kind of partial type signatures. The suggested notation for this is

fn :: [Char] -> _
fn = (2*) . length

Where the _ means "there's a type here, but I can't be bothered to write it out".

It looks like a very easy feature to implement (instantiate _ with unification variables in the signature), but nobody has bothered to work out the semantic details and the interaction with other features.

Solution 4:

To specify only the type of an argument, you can write something like

fn list = 2 * length list
  where
    a :: [Char]
    a = list `asTypeOf` a

So that it is easy to later amend it like, e.g.,

fn list = 2 * fromIntegral (length list)
  where
    a :: [Char]
    a = list `asTypeOf` a

and have its inferred type change accordingly:

*Main> :t fn
fn :: [Char] -> Int
*Main> :r
-- changed file reloaded
*Main> :t fn
fn :: (Num t) => [Char] -> t

You could use the same contorted technique to specify the return type of a function, perhaps defined in pointfree style, but this is not pretty.

fn2 list = r
  where
    r :: Int
    r = f list
    f = (2 *) . length

It is also not much different from what you have right now, just keeps the code and the type spec separated.