[Pitch] Constrains for numeric types: Int<1...10> Double<0.0...1.0>


(Adrian Zubarev) #1

Hello Swift community. I'd like to discuss with you if we need something like this in Swift 3 or any future Swift version.

As you may know there is no way to constrain a numeric type expect for some scope internal assertion or precodintions which may produce a runtime error if the input value is out of the defined bound.

func foo(value: Int) {
    
  assert(value > 0 && value <= 10)

  // passed
}

How would it be if Swift would allow us to constraint numeric typs with ranges/intervals?

func newFoo(value: Int<1...10>) {
    
  // no need for an assertion any more
}

We could go even further and add more then one range/interval:

func someFoo(value: Int<0...20, 40...60>) { /* do some work */ }

Not only integers should have this ability but also floating point types like Double and Float.

Alternative form might look like this:

Double[1.0...10.0]
Float[0.0...1.0, 10.0...100.0]

One downside of half opened ranges/intervals is the left side of its set. How do we exclude the left element?

1...10 means 1..<11 equals [1, 11)

But how can we create something like (0.0, 1.0), do we need a strange looking binary operator 0.0>..<1.0?

What do you think? I'd love to hear any feedback to this.

···

--
Adrian Zubarev
Sent with Airmail


(Adrian Zubarev) #2

Okay I’m fine with that for now. If you’d have to decide on some syntax for such a future, how would it look like? I’m just curious.

I tend to square brackets Double[0.0 … 1.0], because otherwise it might look like a generic type, but I’m not sure if this type refinement could be applied to other types as well so we actually would stick to the generic type syntax here Float<-1.0 … 1.0>.

···

--
Adrian Zubarev
Sent with Airmail

Am 11. Mai 2016 bei 19:54:09, Matthew Johnson (matthew@anandabits.com) schrieb:

This is called a refinement type. It would be cool to explore that direction in the future but it is definitely well out of scope for Swift 3.

Sent from my iPad

On May 11, 2016, at 12:45 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community. I'd like to discuss with you if we need something like this in Swift 3 or any future Swift version.

As you may know there is no way to constrain a numeric type expect for some scope internal assertion or precodintions which may produce a runtime error if the input value is out of the defined bound.

func foo(value: Int) {
assert(value > 0 && value <= 10)

// passed
}

How would it be if Swift would allow us to constraint numeric typs with ranges/intervals?

func newFoo(value: Int<1...10>) {
// no need for an assertion any more
}

We could go even further and add more then one range/interval:

func someFoo(value: Int<0...20, 40...60>) { /* do some work */ }

Not only integers should have this ability but also floating point types like Double and Float.

Alternative form might look like this:

Double[1.0...10.0]
Float[0.0...1.0, 10.0...100.0]

One downside of half opened ranges/intervals is the left side of its set. How do we exclude the left element?

1...10 means 1..<11 equals [1, 11)

But how can we create something like (0.0, 1.0), do we need a strange looking binary operator 0.0>..<1.0?

What do you think? I'd love to hear any feedback to this.

--
Adrian Zubarev
Sent with Airmail
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Rönnqvist) #3

It reminds me of "Refinement Types" (see for example [this blog post][1] or [this paper][2]).

I generally think it’s a cool idea and that it can be useful in minimizing partial functions by requiring that these cases are explicitly handled.
For example, highlighting that the following `average` implementation divides by zero when the list is empty:

func average(numbers: [Int]) -> Int {
    return sum(numbers) / numbers.count
}

and requiring that the empty list case is handled separately:

func average(numbers: [Int]) -> Int {
    guard !numbers.isEmpty else { return 0 }
    return sum(numbers) / numbers.count
}

Regards,
David

[1]: http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/
[2]: http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf

···

On 11 May 2016, at 20:00, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Okay I’m fine with that for now. If you’d have to decide on some syntax for such a future, how would it look like? I’m just curious.

I tend to square brackets Double[0.0 … 1.0], because otherwise it might look like a generic type, but I’m not sure if this type refinement could be applied to other types as well so we actually would stick to the generic type syntax here Float<-1.0 … 1.0>.

--
Adrian Zubarev
Sent with Airmail

Am 11. Mai 2016 bei 19:54:09, Matthew Johnson (matthew@anandabits.com <mailto:matthew@anandabits.com>) schrieb:

This is called a refinement type. It would be cool to explore that direction in the future but it is definitely well out of scope for Swift 3.

Sent from my iPad

On May 11, 2016, at 12:45 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello Swift community. I'd like to discuss with you if we need something like this in Swift 3 or any future Swift version.

As you may know there is no way to constrain a numeric type expect for some scope internal assertion or precodintions which may produce a runtime error if the input value is out of the defined bound.

func foo(value: Int) {
assert(value > 0 && value <= 10)

// passed
}

How would it be if Swift would allow us to constraint numeric typs with ranges/intervals?

func newFoo(value: Int<1...10>) {
// no need for an assertion any more
}

We could go even further and add more then one range/interval:

func someFoo(value: Int<0...20, 40...60>) { /* do some work */ }

Not only integers should have this ability but also floating point types like Double and Float.

Alternative form might look like this:

Double[1.0...10.0]
Float[0.0...1.0, 10.0...100.0]

One downside of half opened ranges/intervals is the left side of its set. How do we exclude the left element?

1...10 means 1..<11 equals [1, 11)

But how can we create something like (0.0, 1.0), do we need a strange looking binary operator 0.0>..<1.0?

What do you think? I'd love to hear any feedback to this.

--
Adrian Zubarev
Sent with Airmail
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Vladimir) #4

I generally think it’s a cool idea and that it can be useful in minimizing

> partial functions by requiring that these cases are explicitly handled.

Support this opinion.

Other example where such feature could be useful: some public property in type that can accept values from some interval. For example we have class with transparency property that can be 0.0 ... 0.1:

class MyShape {
   public var transparency: Double = 1.0 // 0.0 ... 0.1
}

yes, we can use willSet/didSet to check this every time. But if we have a number of such properties, we have a lot of repetitive and boilerplate code:

class C {
     var transparancy : Double = 1.0 {
         didSet { check(transparancy, 0.0...1.0) }
     }

     var prop1 : Int = 1 {
         didSet { check(prop1, from: -10...10) }
     }

     var prop2 : Int = 1 {
         didSet { check(prop2, from: 0...100) }
     }
}

, and all will be worse if we also need didSet observers to do some 'useful' work here.
Proposed solution will looks like:

class C {
     var transparancy : Double<0.0...1.0> = 1.0

     var prop1 : Int<-10...10> = 1

     var prop2 : Int<0...100> = 1
}

Probably the alternative could be some kind of `where` or `bounded` clause for numeric types and arguments:

class C {
     var transparancy : Double = 1.0 where 0.0...1.0
     //var transparancy : Double bounded 0.0...1.0 = 1.0

     var prop1 : Int = 1 where -10...10

     var prop2 : Int = 1 where 0.0...1.0

     //var prop3 : Float bounded 0.0..<100.0 = 0.0
}

···

On 18.05.2016 11:30, David Rönnqvist via swift-evolution wrote:

It reminds me of "Refinement Types" (see for example [this blog post][1] or
[this paper][2]).

I generally think it’s a cool idea and that it can be useful in minimizing
partial functions by requiring that these cases are explicitly handled.
For example, highlighting that the following `average` implementation
divides by zero when the list is empty:

    func average(numbers: [Int]) -> Int {
        return sum(numbers) / numbers.count
    }

and requiring that the empty list case is handled separately:

    func average(numbers: [Int]) -> Int {
        guard !numbers.isEmpty else { return 0 }
        return sum(numbers) / numbers.count
    }

Regards,
David

[1]: http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/
<http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/>
[2]: http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf
<http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf>

On 11 May 2016, at 20:00, Adrian Zubarev via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Okay I’m fine with that for now. If you’d have to decide on some syntax
for such a future, how would it look like? I’m just curious.

I tend to square brackets Double[0.0 … 1.0], because otherwise it might
look like a generic type, but I’m not sure if this type refinement could
be applied to other types as well so we actually would stick to the
generic type syntax here Float<-1.0 … 1.0>.

--
Adrian Zubarev
Sent with Airmail

Am 11. Mai 2016 bei 19:54:09, Matthew Johnson (matthew@anandabits.com
<mailto:matthew@anandabits.com>) schrieb:

This is called a refinement type. It would be cool to explore that
direction in the future but it is definitely well out of scope for Swift 3.

Sent from my iPad

On May 11, 2016, at 12:45 PM, Adrian Zubarev via swift-evolution >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello Swift community. I'd like to discuss with you if we need
something like this in Swift 3 or any future Swift version.

As you may know there is no way to constrain a numeric type expect for
some scope internal assertion or precodintions which may produce a
runtime error if the input value is out of the defined bound.

func foo(value: Int) {
assert(value > 0 && value <= 10)

// passed
}

How would it be if Swift would allow us to constraint numeric typs with
ranges/intervals?

func newFoo(value: Int<1...10>) {
// no need for an assertion any more
}

We could go even further and add more then one range/interval:

func someFoo(value: Int<0...20, 40...60>) { /* do some work */ }

Not only integers should have this ability but also floating point
types like Double and Float.

Alternative form might look like this:

Double[1.0...10.0]
Float[0.0...1.0, 10.0...100.0]

One downside of half opened ranges/intervals is the left side of its
set. How do we exclude the left element?

1...10 means 1..<11 equals [1, 11)

But how can we create something like (0.0, 1.0), do we need a strange
looking binary operator 0.0>..<1.0?

What do you think? I'd love to hear any feedback to this.

--
Adrian Zubarev
Sent with Airmail
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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


(Matthew Johnson) #5

By the way, there is a pretty clever library that allows you to approximate this in Swift today. It's more verbose than first class language support would be, but not bad given the lack of language support.

https://github.com/Ben-G/Validated

···

Sent from my iPad

On May 18, 2016, at 7:49 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

As you guys brought my idea back to life and I’ve done some effort of digging into the Swifts generic future I can show you some fresh ideas.

`Refinement Types` could be really handy, but put them aside for a moment.

Actually we could achieve something like Int<minValue, maxValue> in Swift 3. Take a look at this section here: https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generic-value-parameters

There is a slightly problem with this, we cannot rewrite all types to be generic with default parameters. That said it would be nice if we could overload types somehow (Int vs. Int<Range>), but I’m not sure if this idea would suit the language like this.

Anyways we could then have:

struct Int<_ range: Range<Int>> { … }

Sadly this makes the type not safe at compile time but only signals the user that at runtime you can’t use any number our of its range.

--
Adrian Zubarev
Sent with Airmail

Am 18. Mai 2016 bei 14:13:09, Vladimir.S via swift-evolution (swift-evolution@swift.org) schrieb:

> I generally think it’s a cool idea and that it can be useful in minimizing
> partial functions by requiring that these cases are explicitly handled.

Support this opinion.

Other example where such feature could be useful: some public property in
type that can accept values from some interval. For example we have class
with transparency property that can be 0.0 ... 0.1:

class MyShape {
public var transparency: Double = 1.0 // 0.0 ... 0.1
}

yes, we can use willSet/didSet to check this every time. But if we have a
number of such properties, we have a lot of repetitive and boilerplate code:

class C {
var transparancy : Double = 1.0 {
didSet { check(transparancy, 0.0...1.0) }
}

var prop1 : Int = 1 {
didSet { check(prop1, from: -10...10) }
}

var prop2 : Int = 1 {
didSet { check(prop2, from: 0...100) }
}
}

, and all will be worse if we also need didSet observers to do some
'useful' work here.
Proposed solution will looks like:

class C {
var transparancy : Double<0.0...1.0> = 1.0

var prop1 : Int<-10...10> = 1

var prop2 : Int<0...100> = 1
}

Probably the alternative could be some kind of `where` or `bounded` clause
for numeric types and arguments:

class C {
var transparancy : Double = 1.0 where 0.0...1.0
//var transparancy : Double bounded 0.0...1.0 = 1.0

var prop1 : Int = 1 where -10...10

var prop2 : Int = 1 where 0.0...1.0

//var prop3 : Float bounded 0.0..<100.0 = 0.0
}

On 18.05.2016 11:30, David Rönnqvist via swift-evolution wrote:
> It reminds me of "Refinement Types" (see for example [this blog post][1] or
> [this paper][2]).
>
> I generally think it’s a cool idea and that it can be useful in minimizing
> partial functions by requiring that these cases are explicitly handled.
> For example, highlighting that the following `average` implementation
> divides by zero when the list is empty:
>
> func average(numbers: [Int]) -> Int {
> return sum(numbers) / numbers.count
> }
>
>
> and requiring that the empty list case is handled separately:
>
> func average(numbers: [Int]) -> Int {
> guard !numbers.isEmpty else { return 0 }
> return sum(numbers) / numbers.count
> }
>
>
> Regards,
> David
>
> [1]: http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/
> <http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/>
> [2]: http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf
> <http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf>
>
>
>> On 11 May 2016, at 20:00, Adrian Zubarev via swift-evolution >> >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>
>> Okay I’m fine with that for now. If you’d have to decide on some syntax
>> for such a future, how would it look like? I’m just curious.
>>
>> I tend to square brackets Double[0.0 … 1.0], because otherwise it might
>> look like a generic type, but I’m not sure if this type refinement could
>> be applied to other types as well so we actually would stick to the
>> generic type syntax here Float<-1.0 … 1.0>.
>>
>> --
>> Adrian Zubarev
>> Sent with Airmail
>>
>> Am 11. Mai 2016 bei 19:54:09, Matthew Johnson (matthew@anandabits.com
>> <mailto:matthew@anandabits.com>) schrieb:
>>
>>> This is called a refinement type. It would be cool to explore that
>>> direction in the future but it is definitely well out of scope for Swift 3.
>>>
>>> Sent from my iPad
>>>
>>> On May 11, 2016, at 12:45 PM, Adrian Zubarev via swift-evolution >> >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>
>>>> Hello Swift community. I'd like to discuss with you if we need
>>>> something like this in Swift 3 or any future Swift version.
>>>>
>>>> As you may know there is no way to constrain a numeric type expect for
>>>> some scope internal assertion or precodintions which may produce a
>>>> runtime error if the input value is out of the defined bound.
>>>>
>>>> func foo(value: Int) {
>>>> assert(value > 0 && value <= 10)
>>>>
>>>> // passed
>>>> }
>>>>
>>>> How would it be if Swift would allow us to constraint numeric typs with
>>>> ranges/intervals?
>>>>
>>>> func newFoo(value: Int<1...10>) {
>>>> // no need for an assertion any more
>>>> }
>>>>
>>>> We could go even further and add more then one range/interval:
>>>>
>>>> func someFoo(value: Int<0...20, 40...60>) { /* do some work */ }
>>>>
>>>> Not only integers should have this ability but also floating point
>>>> types like Double and Float.
>>>>
>>>> Alternative form might look like this:
>>>>
>>>> Double[1.0...10.0]
>>>> Float[0.0...1.0, 10.0...100.0]
>>>>
>>>> One downside of half opened ranges/intervals is the left side of its
>>>> set. How do we exclude the left element?
>>>>
>>>> 1...10 means 1..<11 equals [1, 11)
>>>>
>>>> But how can we create something like (0.0, 1.0), do we need a strange
>>>> looking binary operator 0.0>..<1.0?
>>>>
>>>> What do you think? I'd love to hear any feedback to this.
>>>>
>>>> --
>>>> Adrian Zubarev
>>>> Sent with Airmail
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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


(Adrian Zubarev) #6

As you guys brought my idea back to life and I’ve done some effort of digging into the Swifts generic future I can show you some fresh ideas.

`Refinement Types` could be really handy, but put them aside for a moment.

Actually we could achieve something like Int<minValue, maxValue> in Swift 3. Take a look at this section here: https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generic-value-parameters

There is a slightly problem with this, we cannot rewrite all types to be generic with default parameters. That said it would be nice if we could overload types somehow (Int vs. Int<Range>), but I’m not sure if this idea would suit the language like this.

Anyways we could then have:

struct Int<_ range: Range<Int>> { … }

Sadly this makes the type not safe at compile time but only signals the user that at runtime you can’t use any number our of its range.

···

--
Adrian Zubarev
Sent with Airmail

Am 18. Mai 2016 bei 14:13:09, Vladimir.S via swift-evolution (swift-evolution@swift.org) schrieb:

I generally think it’s a cool idea and that it can be useful in minimizing
partial functions by requiring that these cases are explicitly handled.

Support this opinion.

Other example where such feature could be useful: some public property in
type that can accept values from some interval. For example we have class
with transparency property that can be 0.0 ... 0.1:

class MyShape {
public var transparency: Double = 1.0 // 0.0 ... 0.1
}

yes, we can use willSet/didSet to check this every time. But if we have a
number of such properties, we have a lot of repetitive and boilerplate code:

class C {
var transparancy : Double = 1.0 {
didSet { check(transparancy, 0.0...1.0) }
}

var prop1 : Int = 1 {
didSet { check(prop1, from: -10...10) }
}

var prop2 : Int = 1 {
didSet { check(prop2, from: 0...100) }
}
}

, and all will be worse if we also need didSet observers to do some
'useful' work here.
Proposed solution will looks like:

class C {
var transparancy : Double<0.0...1.0> = 1.0

var prop1 : Int<-10...10> = 1

var prop2 : Int<0...100> = 1
}

Probably the alternative could be some kind of `where` or `bounded` clause
for numeric types and arguments:

class C {
var transparancy : Double = 1.0 where 0.0...1.0
//var transparancy : Double bounded 0.0...1.0 = 1.0

var prop1 : Int = 1 where -10...10

var prop2 : Int = 1 where 0.0...1.0

//var prop3 : Float bounded 0.0..<100.0 = 0.0
}

On 18.05.2016 11:30, David Rönnqvist via swift-evolution wrote:

It reminds me of "Refinement Types" (see for example [this blog post][1] or
[this paper][2]).

I generally think it’s a cool idea and that it can be useful in minimizing
partial functions by requiring that these cases are explicitly handled.
For example, highlighting that the following `average` implementation
divides by zero when the list is empty:

func average(numbers: [Int]) -> Int {
return sum(numbers) / numbers.count
}

and requiring that the empty list case is handled separately:

func average(numbers: [Int]) -> Int {
guard !numbers.isEmpty else { return 0 }
return sum(numbers) / numbers.count
}

Regards,
David

[1]: http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/
<http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/>
[2]: http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf
<http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf>

On 11 May 2016, at 20:00, Adrian Zubarev via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Okay I’m fine with that for now. If you’d have to decide on some syntax
for such a future, how would it look like? I’m just curious.

I tend to square brackets Double[0.0 … 1.0], because otherwise it might
look like a generic type, but I’m not sure if this type refinement could
be applied to other types as well so we actually would stick to the
generic type syntax here Float<-1.0 … 1.0>.

--
Adrian Zubarev
Sent with Airmail

Am 11. Mai 2016 bei 19:54:09, Matthew Johnson (matthew@anandabits.com
<mailto:matthew@anandabits.com>) schrieb:

This is called a refinement type. It would be cool to explore that
direction in the future but it is definitely well out of scope for Swift 3.

Sent from my iPad

On May 11, 2016, at 12:45 PM, Adrian Zubarev via swift-evolution >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello Swift community. I'd like to discuss with you if we need
something like this in Swift 3 or any future Swift version.

As you may know there is no way to constrain a numeric type expect for
some scope internal assertion or precodintions which may produce a
runtime error if the input value is out of the defined bound.

func foo(value: Int) {
assert(value > 0 && value <= 10)

// passed
}

How would it be if Swift would allow us to constraint numeric typs with
ranges/intervals?

func newFoo(value: Int<1...10>) {
// no need for an assertion any more
}

We could go even further and add more then one range/interval:

func someFoo(value: Int<0...20, 40...60>) { /* do some work */ }

Not only integers should have this ability but also floating point
types like Double and Float.

Alternative form might look like this:

Double[1.0...10.0]
Float[0.0...1.0, 10.0...100.0]

One downside of half opened ranges/intervals is the left side of its
set. How do we exclude the left element?

1...10 means 1..<11 equals [1, 11)

But how can we create something like (0.0, 1.0), do we need a strange
looking binary operator 0.0>..<1.0?

What do you think? I'd love to hear any feedback to this.

--
Adrian Zubarev
Sent with Airmail
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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

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


(Krystof Vasa) #7

How about something like this instead? Seems a bit more general and perhaps more useful (though with numbers it's not as elegant)?

var alpha: Double where alpha >= 0.0 && alpha <= 0.0
var user: MNUser where user.isLoggedIn
var name: String where !name.isEmpty

Krystof

···

On May 18, 2016, at 2:49 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

As you guys brought my idea back to life and I’ve done some effort of digging into the Swifts generic future I can show you some fresh ideas.

`Refinement Types` could be really handy, but put them aside for a moment.

Actually we could achieve something like Int<minValue, maxValue> in Swift 3. Take a look at this section here: https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generic-value-parameters

There is a slightly problem with this, we cannot rewrite all types to be generic with default parameters. That said it would be nice if we could overload types somehow (Int vs. Int<Range>), but I’m not sure if this idea would suit the language like this.

Anyways we could then have:

struct Int<_ range: Range<Int>> { … }

Sadly this makes the type not safe at compile time but only signals the user that at runtime you can’t use any number our of its range.

--
Adrian Zubarev
Sent with Airmail

Am 18. Mai 2016 bei 14:13:09, Vladimir.S via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

> I generally think it’s a cool idea and that it can be useful in minimizing
> partial functions by requiring that these cases are explicitly handled.

Support this opinion.

Other example where such feature could be useful: some public property in
type that can accept values from some interval. For example we have class
with transparency property that can be 0.0 ... 0.1:

class MyShape {
public var transparency: Double = 1.0 // 0.0 ... 0.1
}

yes, we can use willSet/didSet to check this every time. But if we have a
number of such properties, we have a lot of repetitive and boilerplate code:

class C {
var transparancy : Double = 1.0 {
didSet { check(transparancy, 0.0...1.0) }
}

var prop1 : Int = 1 {
didSet { check(prop1, from: -10...10) }
}

var prop2 : Int = 1 {
didSet { check(prop2, from: 0...100) }
}
}

, and all will be worse if we also need didSet observers to do some
'useful' work here.
Proposed solution will looks like:

class C {
var transparancy : Double<0.0...1.0> = 1.0

var prop1 : Int<-10...10> = 1

var prop2 : Int<0...100> = 1
}

Probably the alternative could be some kind of `where` or `bounded` clause
for numeric types and arguments:

class C {
var transparancy : Double = 1.0 where 0.0...1.0
//var transparancy : Double bounded 0.0...1.0 = 1.0

var prop1 : Int = 1 where -10...10

var prop2 : Int = 1 where 0.0...1.0

//var prop3 : Float bounded 0.0..<100.0 = 0.0
}

On 18.05.2016 11:30, David Rönnqvist via swift-evolution wrote:
> It reminds me of "Refinement Types" (see for example [this blog post][1] or
> [this paper][2]).
>
> I generally think it’s a cool idea and that it can be useful in minimizing
> partial functions by requiring that these cases are explicitly handled.
> For example, highlighting that the following `average` implementation
> divides by zero when the list is empty:
>
> func average(numbers: [Int]) -> Int {
> return sum(numbers) / numbers.count
> }
>
>
> and requiring that the empty list case is handled separately:
>
> func average(numbers: [Int]) -> Int {
> guard !numbers.isEmpty else { return 0 }
> return sum(numbers) / numbers.count
> }
>
>
> Regards,
> David
>
> [1]: http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/
> <http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/>
> [2]: http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf
> <http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf>
>
>
>> On 11 May 2016, at 20:00, Adrian Zubarev via swift-evolution >> >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>
>> Okay I’m fine with that for now. If you’d have to decide on some syntax
>> for such a future, how would it look like? I’m just curious.
>>
>> I tend to square brackets Double[0.0 … 1.0], because otherwise it might
>> look like a generic type, but I’m not sure if this type refinement could
>> be applied to other types as well so we actually would stick to the
>> generic type syntax here Float<-1.0 … 1.0>.
>>
>> --
>> Adrian Zubarev
>> Sent with Airmail
>>
>> Am 11. Mai 2016 bei 19:54:09, Matthew Johnson (matthew@anandabits.com
>> <mailto:matthew@anandabits.com>) schrieb:
>>
>>> This is called a refinement type. It would be cool to explore that
>>> direction in the future but it is definitely well out of scope for Swift 3.
>>>
>>> Sent from my iPad
>>>
>>> On May 11, 2016, at 12:45 PM, Adrian Zubarev via swift-evolution >> >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>
>>>> Hello Swift community. I'd like to discuss with you if we need
>>>> something like this in Swift 3 or any future Swift version.
>>>>
>>>> As you may know there is no way to constrain a numeric type expect for
>>>> some scope internal assertion or precodintions which may produce a
>>>> runtime error if the input value is out of the defined bound.
>>>>
>>>> func foo(value: Int) {
>>>> assert(value > 0 && value <= 10)
>>>>
>>>> // passed
>>>> }
>>>>
>>>> How would it be if Swift would allow us to constraint numeric typs with
>>>> ranges/intervals?
>>>>
>>>> func newFoo(value: Int<1...10>) {
>>>> // no need for an assertion any more
>>>> }
>>>>
>>>> We could go even further and add more then one range/interval:
>>>>
>>>> func someFoo(value: Int<0...20, 40...60>) { /* do some work */ }
>>>>
>>>> Not only integers should have this ability but also floating point
>>>> types like Double and Float.
>>>>
>>>> Alternative form might look like this:
>>>>
>>>> Double[1.0...10.0]
>>>> Float[0.0...1.0, 10.0...100.0]
>>>>
>>>> One downside of half opened ranges/intervals is the left side of its
>>>> set. How do we exclude the left element?
>>>>
>>>> 1...10 means 1..<11 equals [1, 11)
>>>>
>>>> But how can we create something like (0.0, 1.0), do we need a strange
>>>> looking binary operator 0.0>..<1.0?
>>>>
>>>> What do you think? I'd love to hear any feedback to this.
>>>>
>>>> --
>>>> Adrian Zubarev
>>>> Sent with Airmail
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Vladimir) #8

Also though about this. As for numbers, this could be used:

var alpha: Double where (0.0...1.0).contains(alpha)

or

var value: Int where value.isIn(0..<100)

if we have

extension Int {
     func isIn(i: HalfOpenInterval<Int>)->Bool {
         return i.contains(self)
     }
}
(it is sad that we can't use `in` name for such function)

But I don't like here repeats of the name of variable/property. In case small names it is OK, but when we use someDescriptiveName - I don't want to repeat them and IMO any repeat is a possibility for error(when you by mistake typed other name). So probably I'd like something like placeholder #it for the same name:

var user: MNUser where #it.isLoggedIn

···

On 18.05.2016 19:34, Krystof Vasa via swift-evolution wrote:

How about something like this instead? Seems a bit more general and perhaps
more useful (though with numbers it's not as elegant)?

var alpha: Double where alpha >= 0.0 && alpha <= 0.0
var user: MNUser where user.isLoggedIn
var name: String where !name.isEmpty

Krystof

On May 18, 2016, at 2:49 PM, Adrian Zubarev via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

As you guys brought my idea back to life and I’ve done some effort of
digging into the Swifts generic future I can show you some fresh ideas.

`Refinement Types` could be really handy, but put them aside for a moment.

Actually we could achieve something like Int<minValue, maxValue> in Swift
3. Take a look at this section
here: https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generic-value-parameters

There is a slightly problem with this, we cannot rewrite all types to be
generic with default parameters. That said it would be nice if we could
overload types somehow (Int vs. Int<Range>), but I’m not sure if this
idea would suit the language like this.

Anyways we could then have:

struct Int<_ range: Range<Int>> { … }

Sadly this makes the type not safe at compile time but only signals the
user that at runtime you can’t use any number our of its range.

--
Adrian Zubarev
Sent with Airmail

Am 18. Mai 2016 bei 14:13:09, Vladimir.S via swift-evolution
(swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

> I generally think it’s a cool idea and that it can be useful in minimizing
> partial functions by requiring that these cases are explicitly handled.

Support this opinion.

Other example where such feature could be useful: some public property in
type that can accept values from some interval. For example we have class
with transparency property that can be 0.0 ... 0.1:

class MyShape {
public var transparency: Double = 1.0 // 0.0 ... 0.1
}

yes, we can use willSet/didSet to check this every time. But if we have a
number of such properties, we have a lot of repetitive and boilerplate code:

class C {
var transparancy : Double = 1.0 {
didSet { check(transparancy, 0.0...1.0) }
}

var prop1 : Int = 1 {
didSet { check(prop1, from: -10...10) }
}

var prop2 : Int = 1 {
didSet { check(prop2, from: 0...100) }
}

, and all will be worse if we also need didSet observers to do some
'useful' work here.
Proposed solution will looks like:

class C {
var transparancy : Double<0.0...1.0> = 1.0

var prop1 : Int<-10...10> = 1

var prop2 : Int<0...100> = 1
}

Probably the alternative could be some kind of `where` or `bounded` clause
for numeric types and arguments:

class C {
var transparancy : Double = 1.0 where 0.0...1.0
//var transparancy : Double bounded 0.0...1.0 = 1.0

var prop1 : Int = 1 where -10...10

var prop2 : Int = 1 where 0.0...1.0

//var prop3 : Float bounded 0.0..<100.0 = 0.0
}

On 18.05.2016 11:30, David Rönnqvist via swift-evolution wrote:
> It reminds me of "Refinement Types" (see for example [this blog post][1] or
> [this paper][2]).
>
> I generally think it’s a cool idea and that it can be useful in minimizing
> partial functions by requiring that these cases are explicitly handled.
> For example, highlighting that the following `average` implementation
> divides by zero when the list is empty:
>
> func average(numbers: [Int]) -> Int {
> return sum(numbers) / numbers.count
> }
>
> and requiring that the empty list case is handled separately:
>
> func average(numbers: [Int]) -> Int {
> guard !numbers.isEmpty else { return 0 }
> return sum(numbers) / numbers.count
> }
>
> Regards,
> David
>
> [1]: http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/
<http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/>
> <http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/>
> [2]: http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf
<http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf>
> <http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf>
>
>> On 11 May 2016, at 20:00, Adrian Zubarev via swift-evolution >>> >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>> <mailto:swift-evolution@swift.org>> wrote:
>>
>> Okay I’m fine with that for now. If you’d have to decide on some syntax
>> for such a future, how would it look like? I’m just curious.
>>
>> I tend to square brackets Double[0.0 … 1.0], because otherwise it might
>> look like a generic type, but I’m not sure if this type refinement could
>> be applied to other types as well so we actually would stick to the
>> generic type syntax here Float<-1.0 … 1.0>.
>>
>> --
>> Adrian Zubarev
>> Sent with Airmail
>>
>> Am 11. Mai 2016 bei 19:54:09, Matthew Johnson (matthew@anandabits.com <mailto:matthew@anandabits.com>
>> <mailto:matthew@anandabits.com>) schrieb:
>>
>>> This is called a refinement type. It would be cool to explore that
>>> direction in the future but it is definitely well out of scope for Swift 3.
>>>
>>> Sent from my iPad
>>>
>>> On May 11, 2016, at 12:45 PM, Adrian Zubarev via swift-evolution >>> >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>> <mailto:swift-evolution@swift.org>> wrote:
>>>
>>>> Hello Swift community. I'd like to discuss with you if we need
>>>> something like this in Swift 3 or any future Swift version.
>>>>
>>>> As you may know there is no way to constrain a numeric type expect for
>>>> some scope internal assertion or precodintions which may produce a
>>>> runtime error if the input value is out of the defined bound.
>>>>
>>>> func foo(value: Int) {
>>>> assert(value > 0 && value <= 10)
>>>>
>>>> // passed
>>>> }
>>>>
>>>> How would it be if Swift would allow us to constraint numeric typs with
>>>> ranges/intervals?
>>>>
>>>> func newFoo(value: Int<1...10>) {
>>>> // no need for an assertion any more
>>>> }
>>>>
>>>> We could go even further and add more then one range/interval:
>>>>
>>>> func someFoo(value: Int<0...20, 40...60>) { /* do some work */ }
>>>>
>>>> Not only integers should have this ability but also floating point
>>>> types like Double and Float.
>>>>
>>>> Alternative form might look like this:
>>>>
>>>> Double[1.0...10.0]
>>>> Float[0.0...1.0, 10.0...100.0]
>>>>
>>>> One downside of half opened ranges/intervals is the left side of its
>>>> set. How do we exclude the left element?
>>>>
>>>> 1...10 means 1..<11 equals [1, 11)
>>>>
>>>> But how can we create something like (0.0, 1.0), do we need a strange
>>>> looking binary operator 0.0>..<1.0?
>>>>
>>>> What do you think? I'd love to hear any feedback to this.
>>>>
>>>> --
>>>> Adrian Zubarev
>>>> Sent with Airmail
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
<mailto:swift-evolution@swift.org>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
<mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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


(Krystof Vasa) #9

Or just $0 as in closures or newValue as in setters?

BTW this proposal would be simpler to implement IMHO, since the following code:

var user: MNUser where #statement

Would just become

var user: MNUser {
    didSet { assert(#statement) }
}

And with regular arguments, these assertions would get prepended to the funtion body.

It would get a bit more complicated were this on protocols:

protocol MyProtocol {
   var alpha: Double where alpha >= 0.0 { get set }
}

···

18. 5. 2016 v 19:18, Vladimir.S via swift-evolution <swift-evolution@swift.org>:

Also though about this. As for numbers, this could be used:

var alpha: Double where (0.0...1.0).contains(alpha)

or

var value: Int where value.isIn(0..<100)

if we have

extension Int {
   func isIn(i: HalfOpenInterval<Int>)->Bool {
       return i.contains(self)
   }
}
(it is sad that we can't use `in` name for such function)

But I don't like here repeats of the name of variable/property. In case small names it is OK, but when we use someDescriptiveName - I don't want to repeat them and IMO any repeat is a possibility for error(when you by mistake typed other name). So probably I'd like something like placeholder #it for the same name:

var user: MNUser where #it.isLoggedIn

On 18.05.2016 19:34, Krystof Vasa via swift-evolution wrote:
How about something like this instead? Seems a bit more general and perhaps
more useful (though with numbers it's not as elegant)?

var alpha: Double where alpha >= 0.0 && alpha <= 0.0
var user: MNUser where user.isLoggedIn
var name: String where !name.isEmpty

Krystof

On May 18, 2016, at 2:49 PM, Adrian Zubarev via swift-evolution >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

As you guys brought my idea back to life and I’ve done some effort of
digging into the Swifts generic future I can show you some fresh ideas.

`Refinement Types` could be really handy, but put them aside for a moment.

Actually we could achieve something like Int<minValue, maxValue> in Swift
3. Take a look at this section
here: https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generic-value-parameters

There is a slightly problem with this, we cannot rewrite all types to be
generic with default parameters. That said it would be nice if we could
overload types somehow (Int vs. Int<Range>), but I’m not sure if this
idea would suit the language like this.

Anyways we could then have:

struct Int<_ range: Range<Int>> { … }

Sadly this makes the type not safe at compile time but only signals the
user that at runtime you can’t use any number our of its range.

--
Adrian Zubarev
Sent with Airmail

Am 18. Mai 2016 bei 14:13:09, Vladimir.S via swift-evolution
(swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

> I generally think it’s a cool idea and that it can be useful in minimizing
> partial functions by requiring that these cases are explicitly handled.

Support this opinion.

Other example where such feature could be useful: some public property in
type that can accept values from some interval. For example we have class
with transparency property that can be 0.0 ... 0.1:

class MyShape {
public var transparency: Double = 1.0 // 0.0 ... 0.1
}

yes, we can use willSet/didSet to check this every time. But if we have a
number of such properties, we have a lot of repetitive and boilerplate code:

class C {
var transparancy : Double = 1.0 {
didSet { check(transparancy, 0.0...1.0) }
}

var prop1 : Int = 1 {
didSet { check(prop1, from: -10...10) }
}

var prop2 : Int = 1 {
didSet { check(prop2, from: 0...100) }
}
}

, and all will be worse if we also need didSet observers to do some
'useful' work here.
Proposed solution will looks like:

class C {
var transparancy : Double<0.0...1.0> = 1.0

var prop1 : Int<-10...10> = 1

var prop2 : Int<0...100> = 1
}

Probably the alternative could be some kind of `where` or `bounded` clause
for numeric types and arguments:

class C {
var transparancy : Double = 1.0 where 0.0...1.0
//var transparancy : Double bounded 0.0...1.0 = 1.0

var prop1 : Int = 1 where -10...10

var prop2 : Int = 1 where 0.0...1.0

//var prop3 : Float bounded 0.0..<100.0 = 0.0
}

On 18.05.2016 11:30, David Rönnqvist via swift-evolution wrote:
> It reminds me of "Refinement Types" (see for example [this blog post][1] or
> [this paper][2]).
>
> I generally think it’s a cool idea and that it can be useful in minimizing
> partial functions by requiring that these cases are explicitly handled.
> For example, highlighting that the following `average` implementation
> divides by zero when the list is empty:
>
> func average(numbers: [Int]) -> Int {
> return sum(numbers) / numbers.count
> }
>
>
> and requiring that the empty list case is handled separately:
>
> func average(numbers: [Int]) -> Int {
> guard !numbers.isEmpty else { return 0 }
> return sum(numbers) / numbers.count
> }
>
>
> Regards,
> David
>
> [1]: http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/
<http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/>
> <http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/>
> [2]: http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf
<http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf>
> <http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf>
>
>
>> On 11 May 2016, at 20:00, Adrian Zubarev via swift-evolution >>>> >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>> <mailto:swift-evolution@swift.org>> wrote:
>>
>> Okay I’m fine with that for now. If you’d have to decide on some syntax
>> for such a future, how would it look like? I’m just curious.
>>
>> I tend to square brackets Double[0.0 … 1.0], because otherwise it might
>> look like a generic type, but I’m not sure if this type refinement could
>> be applied to other types as well so we actually would stick to the
>> generic type syntax here Float<-1.0 … 1.0>.
>>
>> --
>> Adrian Zubarev
>> Sent with Airmail
>>
>> Am 11. Mai 2016 bei 19:54:09, Matthew Johnson (matthew@anandabits.com <mailto:matthew@anandabits.com>
>> <mailto:matthew@anandabits.com>) schrieb:
>>
>>> This is called a refinement type. It would be cool to explore that
>>> direction in the future but it is definitely well out of scope for Swift 3.
>>>
>>> Sent from my iPad
>>>
>>> On May 11, 2016, at 12:45 PM, Adrian Zubarev via swift-evolution >>>> >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>> <mailto:swift-evolution@swift.org>> wrote:
>>>
>>>> Hello Swift community. I'd like to discuss with you if we need
>>>> something like this in Swift 3 or any future Swift version.
>>>>
>>>> As you may know there is no way to constrain a numeric type expect for
>>>> some scope internal assertion or precodintions which may produce a
>>>> runtime error if the input value is out of the defined bound.
>>>>
>>>> func foo(value: Int) {
>>>> assert(value > 0 && value <= 10)
>>>>
>>>> // passed
>>>> }
>>>>
>>>> How would it be if Swift would allow us to constraint numeric typs with
>>>> ranges/intervals?
>>>>
>>>> func newFoo(value: Int<1...10>) {
>>>> // no need for an assertion any more
>>>> }
>>>>
>>>> We could go even further and add more then one range/interval:
>>>>
>>>> func someFoo(value: Int<0...20, 40...60>) { /* do some work */ }
>>>>
>>>> Not only integers should have this ability but also floating point
>>>> types like Double and Float.
>>>>
>>>> Alternative form might look like this:
>>>>
>>>> Double[1.0...10.0]
>>>> Float[0.0...1.0, 10.0...100.0]
>>>>
>>>> One downside of half opened ranges/intervals is the left side of its
>>>> set. How do we exclude the left element?
>>>>
>>>> 1...10 means 1..<11 equals [1, 11)
>>>>
>>>> But how can we create something like (0.0, 1.0), do we need a strange
>>>> looking binary operator 0.0>..<1.0?
>>>>
>>>> What do you think? I'd love to hear any feedback to this.
>>>>
>>>> --
>>>> Adrian Zubarev
>>>> Sent with Airmail
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
<mailto:swift-evolution@swift.org>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
<mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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

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


(Vladimir) #10

Or just $0 as in closures or newValue as in setters?

Yes, seems like the good idea

var alpha: Double where $0 >= 0.0
or
var alpha: Double where newValue >= 0.0

BTW this proposal would be simpler to implement IMHO, since the following code:

var user: MNUser where #statement

Would just become

var user: MNUser {
    didSet { assert(#statement) }
}

I believe we should be still able to use did/willSet for such variables/properties, right? :

var alpha: Double where $0 >= 0.0 {didSet { updateAll() } }

And with regular arguments, these assertions would get prepended to the funtion body.

It would get a bit more complicated were this on protocols:

protocol MyProtocol {
   var alpha: Double where alpha >= 0.0 { get set }
}

Probably we should not to try to move this feature to protocols?

···

On 18.05.2016 20:28, Krystof Vasa wrote:

18. 5. 2016 v 19:18, Vladimir.S via swift-evolution <swift-evolution@swift.org>:

Also though about this. As for numbers, this could be used:

var alpha: Double where (0.0...1.0).contains(alpha)

or

var value: Int where value.isIn(0..<100)

if we have

extension Int {
   func isIn(i: HalfOpenInterval<Int>)->Bool {
       return i.contains(self)
   }
}
(it is sad that we can't use `in` name for such function)

But I don't like here repeats of the name of variable/property. In case small names it is OK, but when we use someDescriptiveName - I don't want to repeat them and IMO any repeat is a possibility for error(when you by mistake typed other name). So probably I'd like something like placeholder #it for the same name:

var user: MNUser where #it.isLoggedIn

On 18.05.2016 19:34, Krystof Vasa via swift-evolution wrote:
How about something like this instead? Seems a bit more general and perhaps
more useful (though with numbers it's not as elegant)?

var alpha: Double where alpha >= 0.0 && alpha <= 0.0
var user: MNUser where user.isLoggedIn
var name: String where !name.isEmpty

Krystof

On May 18, 2016, at 2:49 PM, Adrian Zubarev via swift-evolution >>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

As you guys brought my idea back to life and I’ve done some effort of
digging into the Swifts generic future I can show you some fresh ideas.

`Refinement Types` could be really handy, but put them aside for a moment.

Actually we could achieve something like Int<minValue, maxValue> in Swift
3. Take a look at this section
here: https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generic-value-parameters

There is a slightly problem with this, we cannot rewrite all types to be
generic with default parameters. That said it would be nice if we could
overload types somehow (Int vs. Int<Range>), but I’m not sure if this
idea would suit the language like this.

Anyways we could then have:

struct Int<_ range: Range<Int>> { … }

Sadly this makes the type not safe at compile time but only signals the
user that at runtime you can’t use any number our of its range.

--
Adrian Zubarev
Sent with Airmail

Am 18. Mai 2016 bei 14:13:09, Vladimir.S via swift-evolution
(swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

I generally think it’s a cool idea and that it can be useful in minimizing
partial functions by requiring that these cases are explicitly handled.

Support this opinion.

Other example where such feature could be useful: some public property in
type that can accept values from some interval. For example we have class
with transparency property that can be 0.0 ... 0.1:

class MyShape {
public var transparency: Double = 1.0 // 0.0 ... 0.1
}

yes, we can use willSet/didSet to check this every time. But if we have a
number of such properties, we have a lot of repetitive and boilerplate code:

class C {
var transparancy : Double = 1.0 {
didSet { check(transparancy, 0.0...1.0) }
}

var prop1 : Int = 1 {
didSet { check(prop1, from: -10...10) }
}

var prop2 : Int = 1 {
didSet { check(prop2, from: 0...100) }
}

, and all will be worse if we also need didSet observers to do some
'useful' work here.
Proposed solution will looks like:

class C {
var transparancy : Double<0.0...1.0> = 1.0

var prop1 : Int<-10...10> = 1

var prop2 : Int<0...100> = 1
}

Probably the alternative could be some kind of `where` or `bounded` clause
for numeric types and arguments:

class C {
var transparancy : Double = 1.0 where 0.0...1.0
//var transparancy : Double bounded 0.0...1.0 = 1.0

var prop1 : Int = 1 where -10...10

var prop2 : Int = 1 where 0.0...1.0

//var prop3 : Float bounded 0.0..<100.0 = 0.0
}

On 18.05.2016 11:30, David Rönnqvist via swift-evolution wrote:

It reminds me of "Refinement Types" (see for example [this blog post][1] or
[this paper][2]).

I generally think it’s a cool idea and that it can be useful in minimizing
partial functions by requiring that these cases are explicitly handled.
For example, highlighting that the following `average` implementation
divides by zero when the list is empty:

func average(numbers: [Int]) -> Int {
return sum(numbers) / numbers.count
}

and requiring that the empty list case is handled separately:

func average(numbers: [Int]) -> Int {
guard !numbers.isEmpty else { return 0 }
return sum(numbers) / numbers.count
}

Regards,
David

[1]: http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/

<http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/>

<http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/blog/2013/01/01/refinement-types-101.lhs/>
[2]: http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf

<http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf>

<http://goto.ucsd.edu/~nvazou/refinement_types_for_haskell.pdf>

On 11 May 2016, at 20:00, Adrian Zubarev via swift-evolution >>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>>> <mailto:swift-evolution@swift.org>> wrote:

Okay I’m fine with that for now. If you’d have to decide on some syntax
for such a future, how would it look like? I’m just curious.

I tend to square brackets Double[0.0 … 1.0], because otherwise it might
look like a generic type, but I’m not sure if this type refinement could
be applied to other types as well so we actually would stick to the
generic type syntax here Float<-1.0 … 1.0>.

--
Adrian Zubarev
Sent with Airmail

Am 11. Mai 2016 bei 19:54:09, Matthew Johnson (matthew@anandabits.com <mailto:matthew@anandabits.com>
<mailto:matthew@anandabits.com>) schrieb:

This is called a refinement type. It would be cool to explore that
direction in the future but it is definitely well out of scope for Swift 3.

Sent from my iPad

On May 11, 2016, at 12:45 PM, Adrian Zubarev via swift-evolution >>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>>> <mailto:swift-evolution@swift.org>> wrote:

Hello Swift community. I'd like to discuss with you if we need
something like this in Swift 3 or any future Swift version.

As you may know there is no way to constrain a numeric type expect for
some scope internal assertion or precodintions which may produce a
runtime error if the input value is out of the defined bound.

func foo(value: Int) {
assert(value > 0 && value <= 10)

// passed
}

How would it be if Swift would allow us to constraint numeric typs with
ranges/intervals?

func newFoo(value: Int<1...10>) {
// no need for an assertion any more
}

We could go even further and add more then one range/interval:

func someFoo(value: Int<0...20, 40...60>) { /* do some work */ }

Not only integers should have this ability but also floating point
types like Double and Float.

Alternative form might look like this:

Double[1.0...10.0]
Float[0.0...1.0, 10.0...100.0]

One downside of half opened ranges/intervals is the left side of its
set. How do we exclude the left element?

1...10 means 1..<11 equals [1, 11)

But how can we create something like (0.0, 1.0), do we need a strange
looking binary operator 0.0>..<1.0?

What do you think? I'd love to hear any feedback to this.

--
Adrian Zubarev
Sent with Airmail
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>

<mailto:swift-evolution@swift.org>

https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>

<mailto:swift-evolution@swift.org>

https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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

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