Writing a Swift function that returns itself

  • A+
Category:Languages

I have this piece of code in Python :

def f(x, y):     # do something...     return f 

I'm trying to write this in Swift but can't figure out if it's possible or not. The return type would get infinitely long.

Here's a part of the game I'm trying to recreate written in Python. It's a dice game with multiple commentary functions that get invoked on each round. After every round finishes, the commentary function could return itself but with some changes as well (such as changing variables in the enclosing scope).:

def say_scores(score0, score1):     """A commentary function that announces the score for each player."""     print("Player 0 now has", score0, "and Player 1 now has", score1)     return say_scores  def announce_lead_changes(previous_leader=None):     """Return a commentary function that announces lead changes."""      def say(score0, score1):         if score0 > score1:             leader = 0         elif score1 > score0:             leader = 1         else:             leader = None         if leader != None and leader != previous_leader:             print('Player', leader, 'takes the lead by', abs(score0 - score1))         return announce_lead_changes(leader)     return say  def both(f, g):     """Return a commentary function that says what f says, then what g says."""     def say(score0, score1):         return both(f(score0, score1), g(score0, score1))     return say   def announce_highest(who, previous_high=0, previous_score=0):     """Return a commentary function that announces when WHO's score     increases by more than ever before in the game.     assert who == 0 or who == 1, 'The who argument should indicate a player.'"""     # BEGIN PROBLEM 7     "*** YOUR CODE HERE ***"     def say(score0,score1):         scores = [score0,score1]         score_diff = scores[who]-previous_score         if score_diff > previous_high:             print(score_diff,"point(s)! That's the biggest gain yet for Player",who)             return announce_highest(who,score_diff,scores[who])         return announce_highest(who,previous_high,scores[who])     return say     # END PROBLEM 7 

The play function that repeats until some player reaches some score:

def play(strategy0, strategy1, score0=0, score1=0, dice=six_sided,          goal=GOAL_SCORE, say=silence):     """Simulate a game and return the final scores of both players, with Player     0's score first, and Player 1's score second.      A strategy is a function that takes two total scores as arguments (the     current player's score, and the opponent's score), and returns a number of     dice that the current player will roll this turn.      strategy0:  The strategy function for Player 0, who plays first.     strategy1:  The strategy function for Player 1, who plays second.     score0:     Starting score for Player 0     score1:     Starting score for Player 1     dice:       A function of zero arguments that simulates a dice roll.     goal:       The game ends and someone wins when this score is reached.     say:        The commentary function to call at the end of the first turn.     """     player = 0  # Which player is about to take a turn, 0 (first) or 1 (second)     # BEGIN PROBLEM 5     "*** YOUR CODE HERE ***"     scores = [score0,score1]     strategies = [strategy0,strategy1]     while score0 < goal and score1 < goal:         scores[player] += take_turn(strategies[player](scores[player], scores[other(player)]),         scores[other(player)], dice)          swap = is_swap(scores[player], scores[other(player)])         player = other(player)         if swap:             scores[0],scores[1] = scores[1], scores[0]         score0,score1 = scores[0],scores[1]     # END PROBLEM 5     # BEGIN PROBLEM 6         "*** YOUR CODE HERE ***"         say = say(score0,score1)     # END PROBLEM 6     return score0, score1 

 


Let's try to write such a thing.

func f() {     return f } 

Now the compiler complains because f is not declared to return anything when it does return something.

Okay, let's try to add a return value type i.e. A closure that accepts no parameters and return nothing.

func f() -> (() -> ()) {     return f } 

Now the compiler complains that f is () -> (() -> ()), and so cannot be converted to () -> ().

We should edit the declaration to return a () -> (() -> ()), right?

func f() -> (() -> (() -> ())) {     return f } 

Now f becomes a () -> (() -> (() -> ())), which cannot be converted to a () -> (() -> ())!

See the pattern now? This will continue forever.

Therefore, you can only do this in a type-unsafe way, returning Any:

func f() -> Any { return f } 

Usage:

func f() -> Any {   print("Hello")   return f } (f() as! (() -> Any))() 

The reason why this is possible in python is exactly because Python is weakly typed and you don't need to specify the return type.

Note that I do not encourage you to write this kind of code in Swift. When you code in Swift, try to solve the problem with a Swift mindset. In other words, you should think of another way of solving the problem that does not involve a function like this.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: