Implementing signum


(Rick M) #1

I'm trying to do this:

protocol Signumable
{
  func sgn() -> Self
}

extension Signumable
{
  func
  sgn()
    -> Self
  {
    let pos = 0 < self // Error
    let neg = self < 0 // Error
    let p = pos ? Self(1) : Self(0)
    let n = neg ? Self(1) : Self(0)
    return p - n
  }
}

extension Double : Signumable {}

But I get

Binary operator '<' cannot be applied to operands of type 'Int' and 'Self'

I figure there should be additional constraints on Signumable.

Help would be much appreciated!

···

--
Rick Mann
rmann@latencyzero.com


(Adrian Zubarev) #2

From some older evolution-thread:

extension SignedNumberType {
var sign: Self {
if self == (0 as Self) {
return (0 as Self)
} else if self > (0 as Self) {
return (1 as Self)
}
return (-1 as Self)
}
}

···

--
Adrian Zubarev
Sent with Airmail

Am 20. November 2016 um 03:44:04, Rick Mann via swift-users (swift-users@swift.org(mailto:swift-users@swift.org)) schrieb:

I'm trying to do this:

protocol Signumable
{
func sgn() -> Self
}

extension Signumable
{
func
sgn()
-> Self
{
let pos = 0 < self // Error
let neg = self < 0 // Error
let p = pos ? Self(1) : Self(0)
let n = neg ? Self(1) : Self(0)
return p - n
}
}

extension Double : Signumable {}

But I get

Binary operator '<' cannot be applied to operands of type 'Int' and 'Self'

I figure there should be additional constraints on Signumable.

Help would be much appreciated!

--
Rick Mann
rmann@latencyzero.com

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Hooman Mehr) #3

Let me explain this a bit further:

Swift generic programming is very different from C++ template programming. Swift compiler needs to type-check generic code on spot, not while instantiating the template (because in Swift, instantiation can occur at runtime if the generic code is from a different module).

This means that it should know what operations will be supported on the type being extended. Your `Signumable` defines only a single function. IF you extend `Signumable` to provide an implementation, you can’t refer to any function other than `sgn()`, but you need more to be able to implement sin(). This is don though generic constraints. You need to constrain the type to which your extension applies. The minimal contains that you need are: `ExpressibleByIntegerLiteral` and `Comparable` so that you can write the algorithm as follow:

The way you wrote it, would require more defined operations (subtraction):

protocol Signumable
{
    func sgn() -> Self
}

extension Signumable where Self: ExpressibleByIntegerLiteral & Comparable
{
    func
        sgn()
        -> Self
    {
        if 0 < self { return 1 }
        if self < 0 { return -1 }
        return 0
    }
}

This can potentially work on any integer (including unsigned) and floating point type and any other type that happens to support those two protocols. But it does not work yet, because those types do not automatically conform to `Signumable`. To make them conform you need a bit of boilerplate:

extension Int: Signumable {}
extension Int8: Signumable {}
extension Int16: Signumable {}
extension Int32: Signumable {}
extension Int64: Signumable {}
extension UInt: Signumable {}
extension UInt8: Signumable {}
extension UInt16: Signumable {}
extension UInt32: Signumable {}
extension UInt64: Signumable {}
extension Float32: Signumable {}
extension Float64: Signumable {}
extension Float80: Signumable {}

The problem is, if a new numeric type is defined say a rational number type <https://gist.github.com/hooman/6e08c48e1e06ee19e06e5b09f664f9be>, it won’t conform automatically and you will have to declare its conformance as above. For this reason, it is better to try to find an existing standard protocol to extend instead of introducing your own protocol for such things.

So, as others suggested, it is better to extend the existing `SignedNumber` protocol instead of introducing your own `Signumable`, unless if you need to write other generic algorithms that need your protocol as their constraints.

···

On Nov 19, 2016, at 6:44 PM, Rick Mann via swift-users <swift-users@swift.org> wrote:

I'm trying to do this:

protocol Signumable
{
  func sgn() -> Self
}

extension Signumable
{
  func
  sgn()
    -> Self
  {
    let pos = 0 < self // Error
    let neg = self < 0 // Error
    let p = pos ? Self(1) : Self(0)
    let n = neg ? Self(1) : Self(0)
    return p - n
  }
}

extension Double : Signumable {}

But I get

Binary operator '<' cannot be applied to operands of type 'Int' and 'Self'

I figure there should be additional constraints on Signumable.

Help would be much appreciated!

--
Rick Mann
rmann@latencyzero.com

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Jacob Bandes-Storch) #4

Just played with this and it turns out that due to the implicit nature of
IntegerLiteralConvertible, you don't need any of the "as Self"s:

extension SignedNumber{
    var sign: Self {
        if self == 0 {
            return 0
        } else if self > 0 {
            return 1
        }
        return -1
    }
}

···

On Sat, Nov 19, 2016 at 11:59 PM, Adrian Zubarev via swift-evolution < swift-evolution@swift.org> wrote:

From some older evolution-thread:

extension SignedNumberType {
    var sign: Self {
       if self == (0 as Self) {
          return (0 as Self)
       } else if self > (0 as Self) {
          return (1 as Self)
       }
       return (-1 as Self)
    }
}
--
Adrian Zubarev
Sent with Airmail


(Rick M) #5

Thank you, Hooman, that's very educational.

···

On Nov 20, 2016, at 10:09 , Hooman Mehr <hooman@mac.com> wrote:

Let me explain this a bit further:

Swift generic programming is very different from C++ template programming. Swift compiler needs to type-check generic code on spot, not while instantiating the template (because in Swift, instantiation can occur at runtime if the generic code is from a different module).

This means that it should know what operations will be supported on the type being extended. Your `Signumable` defines only a single function. IF you extend `Signumable` to provide an implementation, you can’t refer to any function other than `sgn()`, but you need more to be able to implement sin(). This is don though generic constraints. You need to constrain the type to which your extension applies. The minimal contains that you need are: `ExpressibleByIntegerLiteral` and `Comparable` so that you can write the algorithm as follow:

The way you wrote it, would require more defined operations (subtraction):

protocol Signumable
{
    func sgn() -> Self
}

extension Signumable where Self: ExpressibleByIntegerLiteral & Comparable
{
    func
        sgn()
        -> Self
    {
        if 0 < self { return 1 }
        if self < 0 { return -1 }
        return 0
    }
}

This can potentially work on any integer (including unsigned) and floating point type and any other type that happens to support those two protocols. But it does not work yet, because those types do not automatically conform to `Signumable`. To make them conform you need a bit of boilerplate:

extension Int: Signumable {}
extension Int8: Signumable {}
extension Int16: Signumable {}
extension Int32: Signumable {}
extension Int64: Signumable {}
extension UInt: Signumable {}
extension UInt8: Signumable {}
extension UInt16: Signumable {}
extension UInt32: Signumable {}
extension UInt64: Signumable {}
extension Float32: Signumable {}
extension Float64: Signumable {}
extension Float80: Signumable {}

The problem is, if a new numeric type is defined say a rational number type, it won’t conform automatically and you will have to declare its conformance as above. For this reason, it is better to try to find an existing standard protocol to extend instead of introducing your own protocol for such things.

So, as others suggested, it is better to extend the existing `SignedNumber` protocol instead of introducing your own `Signumable`, unless if you need to write other generic algorithms that need your protocol as their constraints.

On Nov 19, 2016, at 6:44 PM, Rick Mann via swift-users <swift-users@swift.org> wrote:

I'm trying to do this:

protocol Signumable
{
  func sgn() -> Self
}

extension Signumable
{
  func
  sgn()
    -> Self
  {
    let pos = 0 < self // Error
    let neg = self < 0 // Error
    let p = pos ? Self(1) : Self(0)
    let n = neg ? Self(1) : Self(0)
    return p - n
  }
}

extension Double : Signumable {}

But I get

Binary operator '<' cannot be applied to operands of type 'Int' and 'Self'

I figure there should be additional constraints on Signumable.

Help would be much appreciated!

--
Rick Mann
rmann@latencyzero.com

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

--
Rick Mann
rmann@latencyzero.com


(Dave Abrahams) #6

To be clear, that's true, but it's not the reason we typecheck generic
code at its point-of-definition. The fact that instantiation can occur
at runtime is almost completely an artifact of our implementation model,
rather than a part of how the language is defined.

We typecheck generics at the point of definition because it's the right
thing to do, regardless of your implementation model. The alternative
leaves you writing generic code that appears to compile but actually
only works for the one or two cases you tested it with, and that
generates the infamous C++ “template instantiation backtrace” when it
fails.

···

on Sun Nov 20 2016, Hooman Mehr <swift-users-AT-swift.org> wrote:

Let me explain this a bit further:

Swift generic programming is very different from C++ template
programming. Swift compiler needs to type-check generic code on spot,
not while instantiating the template (because in Swift, instantiation
can occur at runtime if the generic code is from a different module).

--
-Dave


(Dave Abrahams) #7

The deeper generic programming principle here has to do with how
requirements are clustered into protocols and whether that limits what
you can express. Are there any Signumables that can't also efficiently
implement the requirements of SignedNumber (and vice-versa)? If not,
these two things are fundamentally, in some sense, the same protocol and
should be merged. If so, there may be a reason to have a distinct
protocol, so that you can write algorithms constrained to that protocol
that give protocols that depend on the operation.

···

on Sun Nov 20 2016, Hooman Mehr <swift-users-AT-swift.org> wrote:

Let me explain this a bit further:

Swift generic programming is very different from C++ template programming. Swift compiler needs to
type-check generic code on spot, not while instantiating the template (because in Swift,
instantiation can occur at runtime if the generic code is from a different module).

This means that it should know what operations will be supported on the type being extended. Your
`Signumable` defines only a single function. IF you extend `Signumable` to provide an
implementation, you can’t refer to any function other than `sgn()`, but you need more to be able to
implement sin(). This is don though generic constraints. You need to constrain the type to which
your extension applies. The minimal contains that you need are: `ExpressibleByIntegerLiteral` and
`Comparable` so that you can write the algorithm as follow:

The way you wrote it, would require more defined operations (subtraction):

protocol Signumable
{
    func sgn() -> Self
}

extension Signumable where Self: ExpressibleByIntegerLiteral & Comparable
{
    func
        sgn()
        -> Self
    {
        if 0 < self { return 1 }
        if self < 0 { return -1 }
        return 0
    }
}

This can potentially work on any integer (including unsigned) and floating point type and any other
type that happens to support those two protocols. But it does not work yet, because those types do
not automatically conform to `Signumable`. To make them conform you need a bit of boilerplate:

extension Int: Signumable {}
extension Int8: Signumable {}
extension Int16: Signumable {}
extension Int32: Signumable {}
extension Int64: Signumable {}
extension UInt: Signumable {}
extension UInt8: Signumable {}
extension UInt16: Signumable {}
extension UInt32: Signumable {}
extension UInt64: Signumable {}
extension Float32: Signumable {}
extension Float64: Signumable {}
extension Float80: Signumable {}

The problem is, if a new numeric type is defined say a rational number type
<https://gist.github.com/hooman/6e08c48e1e06ee19e06e5b09f664f9be>, it won’t conform automatically
and you will have to declare its conformance as above. For this reason, it is better to try to find
an existing standard protocol to extend instead of introducing your own protocol for such things.

So, as others suggested, it is better to extend the existing `SignedNumber` protocol instead of
introducing your own `Signumable`, unless if you need to write other generic algorithms that need
your protocol as their constraints.

--
-Dave