summaryrefslogtreecommitdiff
path: root/lambda-calcul/haskell/challenges.json
diff options
context:
space:
mode:
authorArnaud Bailly <arnaud@pankzsoft.com>2026-03-21 09:06:47 +0100
committerArnaud Bailly <arnaud@pankzsoft.com>2026-03-21 09:06:47 +0100
commitfade9c496cebbc0caa97821cb85e9e56da31c57f (patch)
tree7dd30194f57fad64de18584c08385e0e7107eb68 /lambda-calcul/haskell/challenges.json
parentcbc3ebfca24c89c475b9970fa525055d8291ee88 (diff)
downloadlambda-nantes-fade9c496cebbc0caa97821cb85e9e56da31c57f.tar.gz
Add more challenges and plans for enhancement of the interpreter
Diffstat (limited to 'lambda-calcul/haskell/challenges.json')
-rw-r--r--lambda-calcul/haskell/challenges.json324
1 files changed, 306 insertions, 18 deletions
diff --git a/lambda-calcul/haskell/challenges.json b/lambda-calcul/haskell/challenges.json
index 4c247af..544bf0a 100644
--- a/lambda-calcul/haskell/challenges.json
+++ b/lambda-calcul/haskell/challenges.json
@@ -1,20 +1,308 @@
[
- {"level":0, "expr": "x"},
- {"level":1, "expr": "y"},
- {"level":2, "expr": "#%foo"},
- {"level":3, "expr": "(lam (x) x)"},
- {"level":4, "expr": "(x y)"},
- {"level":5, "expr": "((lam (x) x) y)"},
- {"level":6, "expr": "((lam (x y) x) z)"},
- {"level":7, "expr": "(((lam (z) x) z) y)"},
- {"level":8, "expr": "((lam (x y) x) z t)"},
- {"level":9, "expr": "((lam (x y) x) z t foo)"},
- {"level":10, "expr": "(let ((id (lam (x) x))) (id z))"},
- {"level":11, "expr": "(let ((id (lam (x) x))) (id z))"},
- {"level":12, "expr": "(let ((False (lam (f t) f))\n (True (lam (f t) t))\n (Not (lam (b) (b True False))))\n (Not True))"},
- {"level":13, "expr": "(let ((False (lam (f t) f))\n (True (lam (f t) t))\n (Not (lam (b) (b True False))))\n (Not (Not True)))"},
- {"level":14, "expr": "(def False (lam (f t) f))\n (def True (lam (f t) t))\n (def Not (lam (b) (b True False)))\n (Not (Not True)))"},
- {"level":15, "expr": "(def False (lam (f t) f))\n (def True (lam (f t) t))\n (def Not (lam (b) (b True False)))"},
- {"level":16, "expr": "(def Or (lam (a b) (a b True)))"},
- {"level":17, "expr": "(Or (Not True) False)"}
+ {
+ "level": 0,
+ "description": "A lone free variable. What does x reduce to?",
+ "expr": "x",
+ "answer": "x"
+ },
+ {
+ "level": 1,
+ "description": "Another free variable. What does y reduce to?",
+ "expr": "y",
+ "answer": "y"
+ },
+ {
+ "level": 2,
+ "description": "A free variable with a fancy name. Identifiers can include special characters like #, %, @, etc.",
+ "expr": "#%foo",
+ "answer": "#%foo"
+ },
+ {
+ "level": 3,
+ "description": "A lambda abstraction: lam (x) x is the identity function. It is already in normal form.",
+ "expr": "(lam (x) x)",
+ "answer": "(lam (x) x)"
+ },
+ {
+ "level": 4,
+ "description": "A function application where both sides are free variables. (x y) has no redex to reduce.",
+ "expr": "(x y)",
+ "answer": "(x y)"
+ },
+ {
+ "level": 5,
+ "description": "Apply the identity function to y. ((lam (x) x) y) performs one beta-reduction step.",
+ "expr": "((lam (x) x) y)",
+ "answer": "y"
+ },
+ {
+ "level": 6,
+ "description": "Multi-argument lambda applied to one argument. lam (x y) x takes two arguments and returns the first (the K combinator). What is the result of applying it to just z?",
+ "expr": "((lam (x y) x) z)",
+ "answer": "(lam (y) z)"
+ },
+ {
+ "level": 7,
+ "description": "A constant function applied to its argument, then the result applied to y. (lam (z) x) ignores z and always returns the free variable x.",
+ "expr": "(((lam (z) x) z) y)",
+ "answer": "(x y)"
+ },
+ {
+ "level": 8,
+ "description": "Apply the two-argument K combinator lam (x y) x to both z and t. It selects its first argument.",
+ "expr": "((lam (x y) x) z t)",
+ "answer": "z"
+ },
+ {
+ "level": 9,
+ "description": "Apply the K combinator to three arguments. Remember that application is left-associative, so this is ((K z t) foo). What does K z t reduce to, and what happens when you apply that to foo?",
+ "expr": "((lam (x y) x) z t foo)",
+ "answer": "(z foo)"
+ },
+ {
+ "level": 10,
+ "description": "A let-binding introduces a local definition. Here id is bound to the identity function. What does (id z) evaluate to?",
+ "expr": "(let ((id (lam (x) x))) (id z))",
+ "answer": "z"
+ },
+ {
+ "level": 11,
+ "description": "The same let-binding as level 10. let desugars into an immediate lambda application: ((lam (id) (id z)) (lam (x) x)).",
+ "expr": "(let ((id (lam (x) x))) (id z))",
+ "answer": "z"
+ },
+ {
+ "level": 12,
+ "description": "Church-encoded booleans: False = lam (f t) f, True = lam (f t) t. A boolean selects one of two arguments — False picks the first, True picks the second. Not flips a boolean by applying it to True and False in swapped order. What is (Not True)?",
+ "expr": "(let ((False (lam (f t) f))\n (True (lam (f t) t))\n (Not (lam (b) (b True False))))\n (Not True))",
+ "answer": "(lam (f t) f)"
+ },
+ {
+ "level": 13,
+ "description": "Double negation. Applying Not twice should return the original boolean. What is (Not (Not True))?",
+ "expr": "(let ((False (lam (f t) f))\n (True (lam (f t) t))\n (Not (lam (b) (b True False))))\n (Not (Not True)))",
+ "answer": "(lam (f t) t)"
+ },
+ {
+ "level": 14,
+ "description": "The same boolean logic as level 13, but using top-level def declarations instead of let. def binds names in the global environment for the rest of the program.",
+ "expr": "(def False (lam (f t) f))\n (def True (lam (f t) t))\n (def Not (lam (b) (b True False)))\n (Not (Not True)))",
+ "answer": "(lam (f t) t)"
+ },
+ {
+ "level": 15,
+ "description": "Three def declarations with no final expression to evaluate. def statements produce no output on their own — they only extend the environment.",
+ "expr": "(def False (lam (f t) f))\n (def True (lam (f t) t))\n (def Not (lam (b) (b True False)))",
+ "answer": ""
+ },
+ {
+ "level": 16,
+ "description": "Define Or in the global environment. Or a b = a b True: if a is True it selects its first argument (b); if a is False it selects its second argument (True). Again, no expression to evaluate.",
+ "expr": "(def Or (lam (a b) (a b True)))",
+ "answer": ""
+ },
+ {
+ "level": 17,
+ "description": "Use the previously defined Or and Not (assumed in scope from prior levels). Evaluate (Or (Not True) False). What boolean does this reduce to?",
+ "expr": "(Or (Not True) False)",
+ "answer": "(Or (Not True) False)"
+ },
+ {
+ "level": 18,
+ "description": "Introduce And. And a b = a False b: if a is True it selects its second argument (b); if a is False it selects its first argument (False). Evaluate (And True True).",
+ "expr": "(def False (lam (f t) f))\n(def True (lam (f t) t))\n(def Or (lam (a b) (a b True)))\n(def And (lam (a b) (a False b)))\n(And True True)",
+ "answer": "(lam (f t) t)"
+ },
+ {
+ "level": 19,
+ "description": "Combine Or, And and Not. Evaluate (And (Or False True) (Not False)). Both sub-expressions must reduce to True for And to return True.",
+ "expr": "(def False (lam (f t) f))\n(def True (lam (f t) t))\n(def Not (lam (b) (b True False)))\n(def Or (lam (a b) (a b True)))\n(def And (lam (a b) (a False b)))\n(And (Or False True) (Not False))",
+ "answer": "(lam (f t) t)"
+ },
+ {
+ "level": 20,
+ "description": "Xor (exclusive or) is True when exactly one argument is True. It can be defined as And (Or a b) (Not (And a b)). Evaluate (Xor True False).",
+ "expr": "(def False (lam (f t) f))\n(def True (lam (f t) t))\n(def Not (lam (b) (b True False)))\n(def And (lam (a b) (a False b)))\n(def Or (lam (a b) (a b True)))\n(def Xor (lam (a b) (And (Or a b) (Not (And a b)))))\n(Xor True False)",
+ "answer": "(lam (f t) t)"
+ },
+ {
+ "level": 21,
+ "description": "Evaluate (Xor False False). Two identical inputs to Xor should always yield False.",
+ "expr": "(def False (lam (f t) f))\n(def True (lam (f t) t))\n(def Not (lam (b) (b True False)))\n(def And (lam (a b) (a False b)))\n(def Or (lam (a b) (a b True)))\n(def Xor (lam (a b) (And (Or a b) (Not (And a b)))))\n(Xor False False)",
+ "answer": "(lam (f t) f)"
+ },
+ {
+ "level": 22,
+ "description": "Church-encoded if-then-else. Because our True selects its second argument and False selects its first, ite must swap the branches: ite c t e = c e t. Evaluate (ite True a b) — which branch is returned?",
+ "expr": "(def False (lam (f t) f))\n(def True (lam (f t) t))\n(def Not (lam (b) (b True False)))\n(def And (lam (a b) (a False b)))\n(def Or (lam (a b) (a b True)))\n(def ite (lam (c t e) (c e t)))\n(ite True a b)",
+ "answer": "a"
+ },
+ {
+ "level": 23,
+ "description": "Evaluate (ite False a b). When the condition is False, the else-branch should be returned.",
+ "expr": "(def False (lam (f t) f))\n(def True (lam (f t) t))\n(def Not (lam (b) (b True False)))\n(def And (lam (a b) (a False b)))\n(def Or (lam (a b) (a b True)))\n(def ite (lam (c t e) (c e t)))\n(ite False a b)",
+ "answer": "b"
+ },
+ {
+ "level": 24,
+ "description": "Church-encoded pairs. pair x y = lam f -> f x y stores two values by passing them to a selector function. fst p = p (lam x y -> x) retrieves the first. Evaluate (fst (pair a b)).",
+ "expr": "(def pair (lam (x y f) (f x y)))\n(def fst (lam (p) (p (lam (x y) x))))\n(def snd (lam (p) (p (lam (x y) y))))\n(fst (pair a b))",
+ "answer": "a"
+ },
+ {
+ "level": 25,
+ "description": "snd p = p (lam x y -> y) retrieves the second element of a Church pair. Evaluate (snd (pair a b)).",
+ "expr": "(def pair (lam (x y f) (f x y)))\n(def fst (lam (p) (p (lam (x y) x))))\n(def snd (lam (p) (p (lam (x y) y))))\n(snd (pair a b))",
+ "answer": "b"
+ },
+ {
+ "level": 26,
+ "description": "Pairs can be nested. (pair a (pair b c)) is a pair whose second element is itself a pair. Evaluate (snd (snd (pair a (pair b c)))).",
+ "expr": "(def pair (lam (x y f) (f x y)))\n(def fst (lam (p) (p (lam (x y) x))))\n(def snd (lam (p) (p (lam (x y) y))))\n(snd (snd (pair a (pair b c))))",
+ "answer": "c"
+ },
+ {
+ "level": 27,
+ "description": "swap exchanges the two elements of a pair: swap p = pair (snd p) (fst p). Evaluate (fst (swap (pair a b))). After swapping, fst should return what was previously snd.",
+ "expr": "(def pair (lam (x y f) (f x y)))\n(def fst (lam (p) (p (lam (x y) x))))\n(def snd (lam (p) (p (lam (x y) y))))\n(def swap (lam (p) (pair (snd p) (fst p))))\n(fst (swap (pair a b)))",
+ "answer": "b"
+ },
+ {
+ "level": 28,
+ "description": "Church numerals encode natural numbers as higher-order functions: n applies its first argument (the successor step) to its second argument (the base) exactly n times. zero = lam s z -> z applies s zero times. Evaluate (zero f x).",
+ "expr": "(def zero (lam (s z) z))\n(zero f x)",
+ "answer": "x"
+ },
+ {
+ "level": 29,
+ "description": "two = lam s z -> s (s z) applies s twice. Evaluate (two f x) — you should see f applied to x twice.",
+ "expr": "(def two (lam (s z) (s (s z))))\n(two f x)",
+ "answer": "(f (f x))"
+ },
+ {
+ "level": 30,
+ "description": "three = lam s z -> s (s (s z)) applies s three times. Evaluate (three f x).",
+ "expr": "(def three (lam (s z) (s (s (s z)))))\n(three f x)",
+ "answer": "(f (f (f x)))"
+ },
+ {
+ "level": 31,
+ "description": "isZero tests whether a Church numeral is zero. isZero n = n (lam x -> False) True: if n > 0 the step function (lam x -> False) is applied at least once, discarding its argument and returning False; if n = 0 the base True is returned. Evaluate (isZero zero).",
+ "expr": "(def False (lam (f t) f))\n(def True (lam (f t) t))\n(def zero (lam (s z) z))\n(def isZero (lam (n) (n (lam (x) False) True)))\n(isZero zero)",
+ "answer": "(lam (f t) t)"
+ },
+ {
+ "level": 32,
+ "description": "Evaluate (isZero three). Three is not zero, so the step function fires and the result is False.",
+ "expr": "(def False (lam (f t) f))\n(def True (lam (f t) t))\n(def three (lam (s z) (s (s (s z)))))\n(def isZero (lam (n) (n (lam (x) False) True)))\n(isZero three)",
+ "answer": "(lam (f t) f)"
+ },
+ {
+ "level": 33,
+ "description": "The I combinator (identity): I x = x. It is the simplest combinator and the building block of the SKI calculus. Evaluate (I a).",
+ "expr": "(def I (lam (x) x))\n(I a)",
+ "answer": "a"
+ },
+ {
+ "level": 34,
+ "description": "The K combinator (constant): K x y = x. It ignores its second argument and always returns its first. Evaluate (K a b).",
+ "expr": "(def K (lam (x y) x))\n(K a b)",
+ "answer": "a"
+ },
+ {
+ "level": 35,
+ "description": "The S combinator (substitution): S f g x = f x (g x). It distributes its third argument over two functions and applies the results. Evaluate (S f g x) — f and g are free variables here.",
+ "expr": "(def S (lam (f g x) ((f x) (g x))))\n(S f g x)",
+ "answer": "(f x (g x))"
+ },
+ {
+ "level": 36,
+ "description": "SKI completeness: S K K encodes the identity function. S K K x = K x (K x) = x. Evaluate (S K K a) and confirm it equals a.",
+ "expr": "(def S (lam (f g x) ((f x) (g x))))\n(def K (lam (x y) x))\n(S K K a)",
+ "answer": "a"
+ },
+ {
+ "level": 37,
+ "description": "The B combinator (composition): B f g x = f (g x). It is function composition. Evaluate (B f g x) — you should see f applied to the result of g applied to x.",
+ "expr": "(def B (lam (f g x) (f (g x))))\n(B f g x)",
+ "answer": "(f (g x))"
+ },
+ {
+ "level": 38,
+ "description": "The C combinator (flip): C f x y = f y x. It flips the order of the last two arguments of a function. Evaluate (C f a b).",
+ "expr": "(def C (lam (f x y) (f y x)))\n(C f a b)",
+ "answer": "(f b a)"
+ },
+ {
+ "level": 39,
+ "description": "The W combinator (duplication): W f x = f x x. It applies f to x twice, duplicating the argument. Evaluate (W f a).",
+ "expr": "(def W (lam (f x) (f x x)))\n(W f a)",
+ "answer": "(f a a)"
+ },
+ {
+ "level": 40,
+ "description": "Scott-encoded lists. nil = lam c n -> n (the empty list). cons h t = lam c n -> c h t stores head and tail directly. head l = l (lam h t -> h) nil extracts the first element. Evaluate (head (cons a nil)).",
+ "expr": "(def nil (lam (c n) n))\n(def cons (lam (h t c n) (c h t)))\n(def head (lam (l) (l (lam (h t) h) nil)))\n(head (cons a nil))",
+ "answer": "a"
+ },
+ {
+ "level": 41,
+ "description": "tail l = l (lam h t -> t) nil retrieves the rest of a Scott-encoded list. Evaluate (head (tail (cons a (cons b nil)))) — the head of the tail of a two-element list is its second element.",
+ "expr": "(def nil (lam (c n) n))\n(def cons (lam (h t c n) (c h t)))\n(def head (lam (l) (l (lam (h t) h) nil)))\n(def tail (lam (l) (l (lam (h t) t) nil)))\n(head (tail (cons a (cons b nil))))",
+ "answer": "b"
+ },
+ {
+ "level": 42,
+ "description": "isNil tests whether a Scott-encoded list is empty. isNil l = l (lam h t -> False) True: a non-empty list immediately calls its cons branch returning False; nil returns its base True. Evaluate (isNil nil).",
+ "expr": "(def False (lam (f t) f))\n(def True (lam (f t) t))\n(def nil (lam (c n) n))\n(def cons (lam (h t c n) (c h t)))\n(def isNil (lam (l) (l (lam (h t) False) True)))\n(isNil nil)",
+ "answer": "(lam (f t) t)"
+ },
+ {
+ "level": 43,
+ "description": "Evaluate (isNil (cons a nil)). A non-empty list is not nil, so the result should be False.",
+ "expr": "(def False (lam (f t) f))\n(def True (lam (f t) t))\n(def nil (lam (c n) n))\n(def cons (lam (h t c n) (c h t)))\n(def isNil (lam (l) (l (lam (h t) False) True)))\n(isNil (cons a nil))",
+ "answer": "(lam (f t) f)"
+ },
+ {
+ "level": 44,
+ "description": "Church-encoded sum types (Either). Left x = lam l r -> l x tags a value as Left; Right x = lam l r -> r x tags it as Right. either f g e = e f g eliminates the sum by applying the matching handler. Evaluate (either f g (Left a)).",
+ "expr": "(def Left (lam (x l r) (l x)))\n(def Right (lam (x l r) (r x)))\n(def either (lam (f g e) (e f g)))\n(either f g (Left a))",
+ "answer": "(f a)"
+ },
+ {
+ "level": 45,
+ "description": "Evaluate (either f g (Right a)). The Right handler g should be applied to a.",
+ "expr": "(def Left (lam (x l r) (l x)))\n(def Right (lam (x l r) (r x)))\n(def either (lam (f g e) (e f g)))\n(either f g (Right a))",
+ "answer": "(g a)"
+ },
+ {
+ "level": 46,
+ "description": "Church-encoded option (Maybe). None = lam n s -> n is the absent case. Some x = lam n s -> s x wraps a value. fromOption default opt = opt default (lam x -> x) returns the wrapped value or the default. Evaluate (fromOption a None).",
+ "expr": "(def None (lam (n s) n))\n(def Some (lam (x n s) (s x)))\n(def fromOption (lam (default opt) (opt default (lam (x) x))))\n(fromOption a None)",
+ "answer": "a"
+ },
+ {
+ "level": 47,
+ "description": "Evaluate (fromOption a (Some b)). The option contains b, so the default a is ignored and b is returned.",
+ "expr": "(def None (lam (n s) n))\n(def Some (lam (x n s) (s x)))\n(def fromOption (lam (default opt) (opt default (lam (x) x))))\n(fromOption a (Some b))",
+ "answer": "b"
+ },
+ {
+ "level": 48,
+ "description": "mapOption applies a function inside an Option without unwrapping it. mapOption f opt = opt None (lam x -> Some (f x)): None maps to None; Some x maps to Some (f x). Evaluate (mapOption f (Some a)) and give its fully reduced form.",
+ "expr": "(def None (lam (n s) n))\n(def Some (lam (x n s) (s x)))\n(def mapOption (lam (f opt) (opt None (lam (x) (Some (f x))))))\n(mapOption f (Some a))",
+ "answer": "(lam (n s) (s (f a)))"
+ },
+ {
+ "level": 49,
+ "description": "The Kleene predecessor uses a pair-stepping trick to compute pred n = n - 1 (with pred zero = zero). Starting from (pair zero zero), each step shifts: (pair (snd p)) (succ (snd p)). After n steps fst of the pair is n-1. Evaluate (pred zero) — it should return zero.",
+ "expr": "(def zero (lam (s z) z))\n(def one (lam (s z) (s z)))\n(def pair (lam (x y f) (f x y)))\n(def fst (lam (p) (p (lam (x y) x))))\n(def snd (lam (p) (p (lam (x y) y))))\n(def succ (lam (n s z) (s ((n s) z))))\n(def pred (lam (n) (fst (n (lam (p) (pair (snd p) (succ (snd p)))) (pair zero zero)))))\n(pred zero)",
+ "answer": "(lam (s z) z)"
+ },
+ {
+ "level": 50,
+ "description": "Evaluate (pred one) using the same Kleene predecessor. After one pair-step starting from (pair zero zero) the fst is still zero. Confirm that the predecessor of one is zero.",
+ "expr": "(def zero (lam (s z) z))\n(def one (lam (s z) (s z)))\n(def pair (lam (x y f) (f x y)))\n(def fst (lam (p) (p (lam (x y) x))))\n(def snd (lam (p) (p (lam (x y) y))))\n(def succ (lam (n s z) (s ((n s) z))))\n(def pred (lam (n) (fst (n (lam (p) (pair (snd p) (succ (snd p)))) (pair zero zero)))))\n(pred one)",
+ "answer": "(lam (s z) z)"
+ }
]