Swift 4.0在作为类型方法的类型中实现自定义运算符和作为全局函数在全局作用域中实现自定义运算符的区别 [英] Swift 4.0 Difference between implementing custom operator inside a type as a type method and in global scope as a global function

查看:108
本文介绍了Swift 4.0在作为类型方法的类型中实现自定义运算符和作为全局函数在全局作用域中实现自定义运算符的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给定一个解析器类型如下:

  public struct Parser< Result> {
internal let parse:(String) - > (结果,字符串)?

public func run(_ string:String) - > (结果,字符串)? {
guard let(result,remaining)= parse(string)else {return nil}
return(result,remaining)
}

public func map< T> ;(_ transform:@escaping(Result) - > T)
- >解析器< T> {
return Parser< T> {input in
guard let(result,remainder)= self.run(input)else {return nil}
return(transform(result),remaining)
}
}

public func跟在< A>(by other:Parser< A>) - >解析器<(结果,A)> {
return Parser<(Result,A)> {input in
guard let(result,remainder)= self.run(input)else {return nil}
guard let(resultA,remainderA)= other.run(remaining)else {return nil}
返回((result,resultA),remainingA)
}
}
}

首先执行如下:

  infix运算符>>> :FunctionCompositionPrecedence 
public func>>> < A,B> (lhs:Parser A,rhs:Parser B)
- >解析器≤(A,B)> {
return lhs.followed(by:rhs)
}

第二个实现如下所示:

  infix运算符>>> :FunctionCompositionPrecedence 
扩展分析器{
public static func>>> < A,B> (lhs:Parser A,rhs:Parser B)
- >解析器≤(A,B)> {
return lhs.followed(by:rhs)
}
}





此外,当我使用第一个实现并编译下面的代码时,编译器报告错误,因为
'map'产生'Parser',而不是预期的上下文结果类型'Parser'

 扩展解析器{
public static func apply< A,B> (_lhs:解析器(A) - > B,_rhs:解析器A) - >解析器< B个{
return(lhs>>> rhs).map {(arg) - > B in let(f,x)= arg;返回f(x)}
}
}

然而,在我使用第二个实现,一切都很好。
我对它们之间的本质细微差别感到困惑。

解决方案

使用


  infix运算符>>> :FunctionCompositionPrecedence 
扩展分析器{
public static func>>> < A,B> (lhs:Parser A,rhs:Parser B)
- >解析器≤(A,B)> {
return lhs.followed(by:rhs)
}
}


您没有提供编译器在调用操作符时推断泛型占位符 Result (实际上我认为编译器应该错误在这里而不是在使用)。请记住,泛型类型的静态方法在这些类型的特化上调用;必须满足占位符(因为它们可以在静态作用域访问)。



所以直接回答


第一次执行和第二次执行有什么不同。


主要区别在于,静态成员,你需要额外的通用占位符 Result ;作为顶级功能,您没有这个功能。



因此,如果您想保留>>> 作为一个静态方法,您需要在运算符实现的签名中使用 Result 占位符,以便编译器可以根据使用情况推断它的类型,例如:

  infix运算符>>> :FunctionCompositionPrecedence 
扩展分析器{

public static func>>> < B个(
lhs:Parser,rhs:Parser< B>
) - >解析器<(结果,B)> {
return lhs.followed(by:rhs)
}
}

现在结果可以从作为运算符的 lhs 传递的参数的类型(<$在这种情况下,c $ c> Parser 是 Parser< Result> 的语法糖。)

请注意,您会遇到类似的问题:


 扩展程序解析器{
public static func apply< A,B> (_lhs:解析器(A) - > B,_rhs:解析器A) - >解析器< B个{
return(lhs>>> rhs).map {(arg) - > B in let(f,x)= arg;返回f(x)}
}
}


你需要在调用时明确地满足 Result 占位符;尽管用于满足它的类型实际上不会被方法使用。



更好的方法是使用 Result
$ b

 扩展名解析器{
}在签名中包含占位符以允许编译器在调用站点推断它:


public static func apply< Arg>(
_ lhs:Parser<(Arg) - > Result> ;, _rhs:Parser< Arg>
) - >解析器<结果> {

return(lhs>>> rhs).map {arg - >结果在
let(f,x)= arg
return f(x)
}
}
}

// .. 。

let p = Parser<(String) - >串GT; {input in({$ 0 + input},hello)}
let p1 = Parser {($ 0,)}

let p2 = Parser.apply(p,p1)
print(p2.run(world)as Any)//可选((hello world,))

或者,更好的是,作为实例方法:

 扩展分析器{

public func apply< A,B>(with rhs:Parser< A>) - >解析器< B个
其中结果==(A) - > B {

return(self>>> rhs).map {arg - > B
let(f,x)= arg
return f(x)
}
}
}

// ... 。

let p = Parser<(String) - >串GT; {输入于({$ 0 + input},hello)}
让p1 = Parser {($ 0,)}

让p2 = p.apply(with:p1)
print(p2.run(world)as Any)//可选((hello world,))


Given a Parser type as following:

public struct Parser<Result> {
  internal let parse: (String) -> (Result, String)?

  public func run(_ string: String) -> (Result, String)? {
    guard let (result, remainder) = parse(string) else { return nil }
    return (result, remainder)
  }

  public func map<T>(_ transform: @escaping (Result) -> T )
    -> Parser<T> {
      return Parser<T> { input in
        guard let (result, remainder) = self.run(input) else { return nil }
        return (transform(result), remainder)
      }
  }

  public func followed<A>(by other: Parser<A>) -> Parser<(Result, A)> {
    return Parser<(Result, A)> {  input in
      guard let (result, remainder) = self.run(input) else { return nil }
      guard let (resultA, remainderA) = other.run(remainder) else { return nil }
      return ((result, resultA), remainderA)
    }
  }
}

First implementation as following:

infix operator >>> : FunctionCompositionPrecedence
public func >>> <A, B> (lhs: Parser<A>, rhs: Parser<B>) 
  -> Parser<(A,B)> {
  return lhs.followed(by: rhs)
}

Second implementation as following:

infix operator >>> : FunctionCompositionPrecedence
extension Parser {
  public static func >>> <A, B> (lhs: Parser<A>, rhs: Parser<B>) 
    -> Parser<(A,B)> {
    return lhs.followed(by: rhs)
  }
}

Reclaim the question as what is the difference between the first implementation and the second one.

Moreover, when I use the first implementation, and compile the following code, the compiler reported an error as "'map' produces 'Parser', not the expected contextual result type 'Parser'"

extension Parser {
  public static func apply <A, B> (_ lhs: Parser<(A)->B>, _ rhs: Parser<A>) -> Parser<B> {
    return (lhs >>> rhs).map{(arg) -> B in let (f, x) = arg; return f(x)}
  }
}

However, after I use the second implementation, everything goes fine. I am so confused about the essential nuances between them.

解决方案

With

infix operator >>> : FunctionCompositionPrecedence
extension Parser {
  public static func >>> <A, B> (lhs: Parser<A>, rhs: Parser<B>) 
    -> Parser<(A,B)> {
    return lhs.followed(by: rhs)
  }
}

You've provided no way for the compiler to infer the generic placeholder Result on calling the operator (really I think the compiler should error here rather than on usage). Remember that static methods on generic types are called on specialisations of those types; the placeholders must be satisfied (as they're accessible at static scope).

So to directly answer

what is the difference between the first implementation and the second one.

The main difference is that as a static member, you have the additional generic placeholder Result that needs to be satisfied; as a top-level function, you don't have that.

So, if you want to keep >>> as a static method, you'll want to use the Result placeholder in the signature of your operator implementation such that the compiler can infer its type on usage, for example:

infix operator >>> : FunctionCompositionPrecedence
extension Parser {

  public static func >>> <B> (
    lhs: Parser, rhs: Parser<B> 
  ) -> Parser<(Result, B)> {
      return lhs.followed(by: rhs)
  }
}

Now Result can be inferred from the type of the argument passed as the lhs of the operator (Parser is syntactic sugar for Parser<Result> in this context).

Note you'll face a similar problem with

extension Parser {
  public static func apply <A, B> (_ lhs: Parser<(A)->B>, _ rhs: Parser<A>) -> Parser<B> {
    return (lhs >>> rhs).map{(arg) -> B in let (f, x) = arg; return f(x)}
  }
}

in that you'll need to explicitly satisfy the Result placeholder when calling; although the type used to satisfy it won't actually be used by the method.

Better would be to use the Result placeholder in the signature to allow the compiler to infer it at the call-site:

extension Parser {

  public static func apply<Arg>(
    _ lhs: Parser<(Arg) -> Result>, _ rhs: Parser<Arg>
  ) -> Parser<Result> {

    return (lhs >>> rhs).map { arg -> Result in
      let (f, x) = arg
      return f(x)
    }
  }
}

// ...

let p = Parser<(String) -> String> { input in ({ $0 + input }, "hello") }
let p1 = Parser { ($0, "") }

let p2 = Parser.apply(p, p1)
print(p2.run(" world") as Any) // Optional(("hello world", ""))

Or, better still, as an instance method:

extension Parser {

  public func apply<A, B>(with rhs: Parser<A>) -> Parser<B>
    where Result == (A) -> B {

    return (self >>> rhs).map { arg -> B in
      let (f, x) = arg
      return f(x)
    }
  }
}

// ...

let p = Parser<(String) -> String> { input in ({ $0 + input }, "hello") }
let p1 = Parser { ($0, "") }

let p2 = p.apply(with: p1)
print(p2.run(" world") as Any) // Optional(("hello world", ""))

这篇关于Swift 4.0在作为类型方法的类型中实现自定义运算符和作为全局函数在全局作用域中实现自定义运算符的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆