Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-5217] Protocol creates ambiguity around KeyPath roots #47793

Open
stephencelis opened this issue Jun 14, 2017 · 2 comments
Open

[SR-5217] Protocol creates ambiguity around KeyPath roots #47793

stephencelis opened this issue Jun 14, 2017 · 2 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself type checker Area → compiler: Semantic analysis

Comments

@stephencelis
Copy link
Contributor

Previous ID SR-5217
Radar rdar://problem/32774776
Original Reporter @stephencelis
Type Bug
Environment

Swift 4

Additional Detail from JIRA
Votes 1
Component/s Compiler
Labels Bug, TypeChecker
Assignee None
Priority Medium

md5: 6441524ea523bbc60a745d0143019e57

relates to:

  • SR-5667 Type inference for Swift 4 KeyPaths fails within collections

Issue Description:

The following snippet throws a compiler error:

precedencegroup FunctionComposition {
  associativity: right
  higherThan: LensGetter
}

precedencegroup FunctionApplication {
  associativity: left
}

precedencegroup LensSetter {
  associativity: right
  higherThan: FunctionApplication
}

precedencegroup LensGetter {
  associativity: right
  higherThan: LensSetter
}

infix operator |>: FunctionApplication
infix operator %~: LensSetter
infix operator .~: LensSetter

infix operator .^: LensGetter

func |> <A, B>(x: A, f: (A) -> B) -> B {
  return f(x)
}

public protocol LensProtocol {
  associatedtype Root
  associatedtype Value
  var get: (Root) -> Value { get }
  var set: (Root, Value) -> Root { get }
}

public struct Lens<Root, Value>: LensProtocol {
  public let get: (Root) -> Value
  public let set: (Root, Value) -> Root
}

extension WritableKeyPath: LensProtocol {
  public var get: (Root) -> Value {
    return { root in root[keyPath: self] }
  }

  public var set: (Root, Value) -> Root {
    return { root, value in
      var copy = root
      copy[keyPath: self] = value
      return copy
    }
  }
}

public func .~ <L: LensProtocol>(lens: L, value: L.Value) -> (L.Root) -> L.Root {
  return { root in lens.set(root, value) }
}

public func %~ <L: LensProtocol>(lens: L, over: @escaping (L.Value) -> L.Value) -> (L.Root) -> L.Root {
  return { root in lens.set(root, over(lens.get(root))) }
}

public func .^ <L: LensProtocol>(root: L.Root, lens: L) -> L.Value {
  return lens.get(root)
}

struct User {
  private(set) var id: Int
  private(set) var name: String

  public init(id: Int, name: String) {
    self.id = id
    self.name = name
  }
}

let incr: (Int) -> Int = { $0 + 1 }
let uppercased: (String) -> String = { $0.uppercased() }

let user = User(id: 1, name: "Stephen")

user
  |> \.id %~ incr
  |> \.name %~ uppercased

Those last few lines, where user |> .id %~ incr is written, work fine when %~ is defined in terms of WritableKeyPath directly. E.g.:

precedencegroup FunctionComposition {
  associativity: right
  higherThan: LensGetter
}

precedencegroup FunctionApplication {
  associativity: left
}

precedencegroup LensSetter {
  associativity: right
  higherThan: FunctionApplication
}

precedencegroup LensGetter {
  associativity: right
  higherThan: LensSetter
}

infix operator |>: FunctionApplication
infix operator %~: LensSetter
infix operator .~: LensSetter

infix operator .^: LensGetter

func |> <A, B>(x: A, f: (A) -> B) -> B {
  return f(x)
}

public func %~ <Root, Value>(path: KeyPath<Root, Value>, over: @escaping (Value) -> Value) -> (Root) -> Root {
  return { root in
    let writablePath = path as! WritableKeyPath<Root, Value>
    var copy = root
    copy[keyPath: writablePath] = over(copy[keyPath: writablePath])
    return copy
  }
}

public func .~ <Root, Value>(path: KeyPath<Root, Value>, value: Value) -> (Root) -> Root {
  return path %~ const(value)
}

struct User {
  private(set) var id: Int
  private(set) var name: String

  public init(id: Int, name: String) {
    self.id = id
    self.name = name
  }
}

let incr: (Int) -> Int = { $0 + 1 }
let uppercased: (String) -> String = { $0.uppercased() }

let user = User(id: 1, name: "Stephen")

user
  |> \.id %~ incr
  |> \.name %~ uppercased

The first, protocol-based example can be disambiguated using the full key-paths:

user
  |> \User.id %~ incr
  |> \User.name %~ uppercased
@belkadan
Copy link
Contributor

cc @jckarter, @huonw

@jckarter
Copy link
Member

@swift-ci create

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself type checker Area → compiler: Semantic analysis
Projects
None yet
Development

No branches or pull requests

3 participants