5

I tried following code, but it generates type errors.

sa f = f f
• Occurs check: cannot construct the infinite type: t ~ t -> t1
• In the first argument of ‘f’, namely ‘f’
  In the expression: f f
  In an equation for ‘sa’: sa f = f f
• Relevant bindings include
    f :: t -> t1
      (bound at fp-through-lambda-calculus-michaelson.hs:9:4)
    sa :: (t -> t1) -> t1
      (bound at fp-through-lambda-calculus-michaelson.hs:9:1)
4
  • 2
    What type do you think sa should have? Remember that all terms in Haskell must have a type. Also, what problem are you trying to solve? Commented Mar 20, 2017 at 6:22
  • @Alec I don't know, it takes a function that can take a function ? I'm just learning lambda calculus and wondered how to express it in Haskell. I thought Haskell would be nice to check each example, but I got stuck immediately. Maybe Lisp or Scheme is easier for this purpose. Commented Mar 20, 2017 at 7:13
  • It's not a matter of which language you use. Try to construct this function in typed lambda calculus and think what type should it have. The problem we'll be similar, you can't construct infinite types Commented Mar 20, 2017 at 11:13
  • It does matter. You can easily do this in Lisp and Scheme, because they are dynamically typed. Commented Mar 20, 2017 at 20:10

3 Answers 3

13

Use a newtype to construct the infinite type.

newtype Eventually a = NotYet (Eventually a -> a)

sa :: Eventually a -> a
sa eventually@(NotYet f) = f eventually

In GHC, eventually and f will be the same object in memory.

Sign up to request clarification or add additional context in comments.

3 Comments

Moreover, this might trigger a known bug in GHC which makes the inliner to diverge. GHC devs will not fix this for pragmatic reasons, and IIRC the bug has an easy workaround using {-# NOINLINE as #-} so to disable the inliner on the critical function.
@Daniel Wagner, Thank you, but how can I apply this sa to, say, identity function id ? I still get type errors when I try something like :type NotYet Main.id
@YoshihiroTanaka, this is a monotyped self-application. In the self application id id, the two ids have different types. More explicitly its (id :: (A -> A) -> (A -> A)) (id :: A -> A) (for any type A). Thus you can't use sa because the thing sa applies to itself is not polymorphic.
3

I don't think there is a single self-application function that will work for all terms in Haskell. Self-application is a peculiar thing in typed lambda calculus, which will often evade typing. This is related to the fact that with self-application we can express the fixed-point combinator, which introduces inconsistencies into the type system when viewed as a logical system (see Curry-Howard correspondence).

You asked about applying it to the id function. In the self application id id, the two ids have different types. More explicitly it's (id :: (A -> A) -> (A -> A)) (id :: A -> A) (for any type A). We could make a self-application specifically designed for the id function:

sa :: (forall a. a -> a) -> b -> b
sa f = f f

ghci> :t sa id
sa id :: b -> b

which works just fine, but is rather limited by its type.

Using RankNTypes you can make families of self-application functions like this, but you're not going to be able to make a general self-application function such that sa t will be well-typed iff t t is well-typed (at least not in System Fω ("F-omega"), which GHC's core calculus is based on).

The reason, if you work it out formally (probably), is that then we could get sa sa, which has no normal form, and Fω is known to be normalizing (until we add fix of course).

2 Comments

Thank you for your help! I'm new to Haskell and did not even know I had to add {-# LANGUAGE RankNTypes #-}. It works now :-)
Also wanted to add a link to relevant post on Haskell type-checker back from 2002: link
1

This is because the untyped lambda calculus is in some way more powerful than Haskell. Or, to put it differently, the untyped lambda calculus has no type system. Thus, it has no sound type system. Whereas Haskell does have one.

This shows up not only with self application, but in any cases where infinite types are involved. Try this, for example:

i x = x
s f g x = f x (g x)
s i i

It is astonishing how the type system finds out that the seemingly harmless expresion s i i should not be allowed with a sound type system. Because, if it were allowed, self application would be possible.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.