Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing a Swift function that returns itself

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
like image 277
Pirlo Lochisomo Avatar asked Apr 21 '26 02:04

Pirlo Lochisomo


2 Answers

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.

like image 73
Sweeper Avatar answered Apr 23 '26 14:04

Sweeper


Not exactly what you want perhaps but you can do something similar with a closure

typealias Closure = (Int) -> Int

func doStuff(action: @escaping Closure, value: Int) -> Closure {
    let x = action(value)
    //do something
    return action
}
like image 25
Joakim Danielson Avatar answered Apr 23 '26 16:04

Joakim Danielson