Reputation: 263078
Just for fun, here is my own version of cycle
:
myCycle :: [a] -> [a]
myCycle xs = xs ++ myCycle xs
The right-hand side refers to both the function name myCycle
and the parameter xs
.
Is it possible to implement myCycle
without mentioning myCycle
or xs
on the right-hand side?
myCycle = magicLambdaFunction
Upvotes: 9
Views: 518
Reputation: 30227
Is it possible to implement
myCycle
without mentioningmyCycle
orxs
on the right-hand side?
The answer is yes and no (not necessarily in that order).
Other people have mentioned the fixed point combinator. If you have a fixed-point combinator fix :: (a -> a) -> a
, then as you mention in a comment to Pubby's answer, you can write myCycle = fix . (++)
.
But the standard definition of fix
is this:
fix :: (a -> a) -> a
fix f = let r = f r in r
-- or alternatively, but less efficient:
fix' f = f (fix' f)
Note that the definition of fix
involves mentioning a left-hand-side variable on the right hand side of its definition (r
in the first definition, fix'
in the second one). So what we've really done so far is push the problem down to just fix
.
The interesting thing to note is that Haskell is based on a typed lambda calculus, and for good technical reason most typed lambda calculi are designed so that they cannot "natively" express the fixed point combinator. These languages only become Turing-complete if you add some extra feature "on top" of the base calculus that allows for computing fixed points. For example, any of these will do:
fix
as a primitive to the calculus.fix
in Haskell).This is a useful type of modularity for many reasons—one being that a lambda calculus without fixed points is also a consistent proof system for logic, another that fix
-less programs in many such systems can be proven to terminate.
EDIT: Here's fix
written with recursive types. Now the definition of fix
itself is not recursive, but the definition of the Rec
type is:
-- | The 'Rec' type is an isomorphism between @Rec a@ and @Rec a -> a@:
--
-- > In :: (Rec a -> a) -> Rec a
-- > out :: Rec a -> (Rec a -> a)
--
-- In simpler words:
--
-- 1. Haskell's type system doesn't allow a function to be applied to itself.
--
-- 2. @Rec a@ is the type of things that can be turned into a function that
-- takes @Rec a@ arguments.
--
-- 3. If you have @foo :: Rec a@, you can apply @foo@ to itself by doing
-- @out foo foo :: a@. And if you have @bar :: Rec a -> a@, you can do
-- @bar (In bar)@.
--
newtype Rec a = In { out :: Rec a -> a }
-- | This version of 'fix' is just the Y combinator, but using the 'Rec'
-- type to get around Haskell's prohibition on self-application (see the
-- expression @out x x@, which is @x@ applied to itself):
fix :: (a -> a) -> a
fix f = (\x -> f (out x x)) (In (\x -> f (out x x)))
Upvotes: 11
Reputation: 69924
No one pointed out the "obvious" version of the fix solution yet. The idea is that you transform the named recursive call into a parameter.
let realMyCycle = fix (\myCycle xs -> xs ++ myCycle xs)
This "recursive name" introducing trick is pretty much what let in
does in Haskell. The only difference is that using the built-in construct is more straightforward and probably nicer for the implementation.
Upvotes: 0
Reputation: 1930
For fun this is another stuff :
let f = foldr (++) [] . repeat
or
let f = foldr1 (++) . repeat
Upvotes: 5
Reputation: 53017
I think this works:
myCycle = \xs -> fix (xs ++)
http://en.wikipedia.org/wiki/Fixed-point_combinator
In programming languages that support anonymous functions, fixed-point combinators allow the definition and use of anonymous recursive functions, i.e. without having to bind such functions to identifiers. In this setting, the use of fixed-point combinators is sometimes called anonymous recursion.
Upvotes: 6