How to define function signatures partially in Haskell?

Starting point:

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

Suppose we want to limit the return value, then we could write:

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

How about limiting just an argument? Easily.

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

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

This is the closest I can think of:

 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 would like a cleaner solution. Something that reports that my intention is a partial restriction. Something like this, for example:

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

Or maybe:

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

Is it possible?

Edit for further clarification:

@GaneshSittampalam Made an important point in the comments below. I am looking for "a house halfway between a type signature and generally should give an exact one." So, I'm not looking for an answer based on TypeClass, I just want the GHC to fill in the blanks for the undefined (or not completely limited) types of my function.

Edit in response to @WillNess

Yes, something like this ...

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

... may work, but only for arguments, and only if the function is not point-wise. Is there a way to apply this method to centerless functions or return values?

Edit in response to @Rhymoid

I got inspiration and played with @Rhymoid 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 limits the signature to fn and does not pollute any namespace.

Usually we will have only one of the lines asTypeOf , I just added a few to demonstrate how powerful this approach is.

This is a bit more awkward than what I would like, but I think it's pretty neat, we can do this even without special syntactic support from the language.

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

+46
haskell
Feb 09 '14 at 11:23
source share
6 answers

Sorry for the self-promotion, but this function is the subject of a recent Ph.D. student Thomas Wyant, myself, Frank Pisens and Tom Shrivers, very recently introduced by Thomas at the PADL 2014 Symposium. For more information, see here . This is a feature that is already present in some other languages, but its interaction with features such as Haskell GADT has made it interesting enough to develop the details.

Thomas is working on a GHC implementation. This has improved since the writing of the article, but the implementation of the “wildcard restriction” in the GHC is technically a bit more complicated than we expected. We expect that we can continue to work and contact the GHC developers to get it, but whether this can happen depends on how many people would like to have this feature in Haskell ...

Update 14-4-2015: After much work by Thomas and materials from SPJ and other GHC people, partial signatures were released in GHC 7.10. Thomas Vinant wrote an introductory blog post on how you can use them.

+36
Feb 09 '14 at 14:40
source share

I was looking for a way to say ' x type unify 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 parsing 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 can even go simply

 fn = (2 *) . length where _ = fn `asTypeOf` (undefined :: [Char] -> a) 
+13
Feb 09 '14 at 15:54
source share

You are looking for a feature that many of us would like, but Haskell does not. Nothing. You need some partial signatures. Suggested notation for this

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

Where _ means "there is a type, but I can not write it."

It seems like a very simple function to implement (instance _ with unification variables in the signature), but no one bothered to develop semantic details and interaction with other functions.

+10
Feb 09 '14 at 11:47
source share

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

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

So it's easy to change it later, for example,

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

and change its type accordingly:

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

You can use the same distorted technique to indicate the return type of the function, possibly defined in pointfree , but it is not.

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

It is also not much different from what you have right now, it just saves the code and type specification.

+6
Feb 09 '14 at 11:40
source share

If your fn type can be automatically inferred without a signature, and you just want the compiler to check if the intended type has the correct form, you can use the following.

The idea is to write something like

 fnSig :: exists _1 _2. forall a. _1 a -> _2 fnSig = fn 

except that Haskell does not allow the existential types above. However, existential types can be emulated using higher rank types as follows:

 {-# LANGUAGE RankNTypes #-} fnSig :: (forall _1 _2. (forall a. _1 a -> _2) -- your actual type, _ are the unknowns ->r)->r fnSig = \k->k fn -- the compiler infers _1=[] , _2=Int -- fn :: [] a -> Int fn = (2 *) . length 

The above “trick” is essentially the same as the one used, for example. runST.

Alternatively, you can declare a special existential data type.

 {-# LANGUAGE GADTs #-} data Ex where Ex :: (forall a. _1 a -> _2) -> Ex fnSig = Ex fn 

which should force the compiler to perform the same type of check.

+2
Feb 09 '14 at 13:01
source share

I think you want really bad. The function you want slightly increases the convenience of type inference, especially for top-level functions. But the signatures of the top-level declarations are significant project contracts. They are APIs, they are documentation, they are beacons for strangers who are included in your code, therefore they should be strong and clear.

Yes, haskell allows type restrictions for return types. But this is mainly for temporary results in let-blocks. And yes, you can use

  f (x :: Int) = 2*x 

with the extension XScopedTypeVariables (but it does not apply to dotless functions).

+1
Feb 10 '14 at 4:58
source share



All Articles