Swift: Returning a recursive function (currying)

Oliver Atkinson

I am wanting to return a function which will in turn call back itself. Is it possible to do through returning a closure calling itself?

My problem is that I'm unsure of the correct syntax to use here, as well as I'm not sure if it is even possible due to having a cyclic reference to itself (and swift being heavy on compiler type checking)

I am currying my functions so that the models and presenters do not need to know about the dataGateway further decoupling my code

Some background information about the problem, the API expects a page number to be passed into itself, I do not want to store this state. I want the function to pass something back so that the model can just call the next function when it needs to.

I know the curried function definition looks like this:

function    (completion: ([Example.Product], UInt) -> Void) -> Example.Task?

look for __function_defined_here__ in my code samples

Original - example code

func fetch(dataGateway: DataGateway, category: String)(page: UInt)(completion: [Product] -> Void) -> Task? {

  return dataGateway.productMap(category, page: page) { products in
    completion(products.map { $0.build })
  }

}

Idea 1 - return as tuple

func fetch(dataGateway: DataGateway, category: String)(page: UInt)(completion: [Product] -> Void) -> (Task?, __function_defined_here__) {

  return (dataGateway.productMap(category, page: page) { products in
    completion(products.map { $0.build })
  }, fetch(dataGateway, category: category)(page: page + 1))

}

Idea 2 - pass back in the completion

func fetch(dataGateway: DataGateway, category: String)(page: UInt)(completion: ([Product], __function_defined_here__) -> Void) -> Task? {

  return dataGateway.productMap(category, page: page) { products in
    completion(products.map { $0.build }, fetch(dataGateway, category: category)(page: page + 1))
  }

}
Oliver Atkinson

I ended up solving it with something like the following, what it does is create a class reference to store the next function in. I pass a reference to this object in the completion of the asynchronous operation.

extension Product {
  class Next {

    typealias FunctionType = (([Product], Next) -> Void) -> Task?
    let fetch: FunctionType

    init(_ fetch: FunctionType) {
      self.fetch = fetch
    }

  }

  func fetch(dataGateway: DataGateway, inCategory category: String)(page: UInt)(completion: ([Product], Next) -> Void) -> Task? {

    return dataGateway.products(inCategory: category, page: page)() { products in
      completion(products.map { $0.build }, Next(fetch(dataGateway, inCategory: category)(page: page + 1)))
    }

  }
}


let initial = Product.fetch(dataGateway, inCategory: "1")(page: 0)

pass the function in to a data model

data() { [weak self] products, next in 
  self?.data = products
  self?.setNeedsUpdate()
  self?.next = next
}

scrolling down to bottom of table view triggers the above again, using the next function instead of data

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related