[swift-evolution] [Proposal Draft] Concise Default Values for Containers

Paul Ossenbruggen possen at gmail.com
Sat Jan 16 17:37:34 CST 2016


This is a preliminary draft, looking for feedback, thanks. 

Introduction

There are many cases where you don’t want to deal with an out of range lookup in a array or dictionary. There is quite a lot of boilerplate code to check the range and return a value. This proposal addresses that by making a concise way of providing a default value in an array or dictionary. You quickly and safely want to get the value from an array or dictionary but not have to write a bunch of checks. 

Swift-evolution thread: derived from ternary discussion. 

Motivation

There are many times when you want to map value to another, the range of input values is beyond the array index. Typically you have to write code like this;

	let dayString = [“Sunday”, “Monday”, “Tuesday”, “Wednesday”, “Thursday”, “Friday”, “Saturday”]
	guard  dayIndex > 0 && dayIndex < col..count else {
		return “invalid day"
	}
       dayString[dayIndex] 

Or for a dictionary:

	let chineseNumber = [   “一” : “One”, 
		     “二” : ”Two”,
					      “三” : ”Three”]

	guard  let englishString = englishString else {
		return “out of range"
	}
	return englishString 

Currently dictionaries only produce “nil” if not found so you must handle an optional. 

Proposed solution:

There approach is to add default to the containers. 

Array:
There is a new syntax which, allows you to choose a default:

let lookupDayOfWeek = [“Sunday”, “Monday”, “Tuesday”, “Wednesday”, “Thursday”, “Friday”, “Saturday” <> “Invalid Day” ]

lookupDayOfWeek[1] -> “Monday”
lookupDayOfWeek[9] -> “Invalid Day”

The <> which is less than greater than. for the else portion, it indicates outside the range of the array as in less than the minimum index and greater than the highest index. It also looks like a diamond when run together so it stands out to clearly show that it is the default case and won’t be confused with the main array. In all other ways the array behaves the same, if you want to get the default value you could do this:

lookupDayOfWeek[ _ ] -> “Invalid Day”

Iterating an array with a default would not include the default:

	for day in lookupDayOfWeek {
		print(“Day \(day)”)
	}

You would need to do this to get the default (see below for slices):

	for day in lookupDayOfWeek[:_] {
		print(“Day \(day)”)
	}

or this would put the default first:

	for day in lookupDayOfWeek[_:] {
		print(“Day \(day)”)
	}

Unless there is a better suggestion for this, as this relies on slices. 

Likewise contains() and other operations would not include the default as a result. However, if you were to copy an array with a default it would travel with the array:

let myOtherArray = lookupDayOfWeek
print(myOtherArray[_])  -> “Invalid Day”

If we were to adopt slices it would be possible to copy just the main values like this (based upon python slice syntax, speculative because slice syntax is not yet defined. If it is;):

let myOtherArray = lookupDayOfWeek[:]
print(myOtherArray) -> [“Sunday”, “Monday”, “Tuesday”, “Wednesday”, “Thursday”, “Friday”, “Saturday”] 

let myOtherArray = lookupDayOfWeek[:_]
print(myOtherArray -> [“Sunday”, “Monday”, “Tuesday”, “Wednesday”, “Thursday”, “Friday”, “Saturday” <> "Invalid Day”]

Dictionary:
For a dictionary, we use the _: to indicate none of the above, which is consistent with current dictionary syntax except that it adds the _ case..

let chineseToEnglishNumber = [   “一" : “One”, 
		         “二": ”Two”,
				                      "三": ”Three”,
				                        _:  “Out of Range” ]

print(chineseToEnglishNumber[ “三”]) -> “Three"
print(chineseToEnglishNumber[“四”]) -> “Out of Range”

This concisely represents what to do if none of the values are usable. No more if let clauses or guards. Diamond <> could be supported here too, but that is optional for this proposal. The dictionary would be handled pretty much as it is today but the underscore-colon lets you give a default. Similarly, you can access the default value by doing this.

print(chineseToEnglishNumber[_]) -> “Out of Range”

print(chineseToEnglishNumber.contains(“None”)) -> would return false. 
print(chineseToEnglishNumber) -> ["一": "2", "二": "1", "三": “3” <> “ Out of Range”]

Copying the dictionary would include the default but iterating it would not include the default: 

for number in chineseToEnglishNumber {
	
}

to include it (this may need work):

for number in chineseToEnglishNumber[:_] {
	
}

since dictionaries are unordered it will not necessarily be at the end..

Alternatives considered

Other operators:
Tried single colon and double colon but did not stand out enough and colon might not work if we adopted slices. 

Sets
Thought about sets but not sure that makes sense because you only test existence of a member usually, the following kind makes sense to a point:

let set = Set(“A”, “B”, “C” <> “D”)
set.contains(“B”) -> true
set.contains(“D”) -> false
set.contains(“F”) -> false

print(set) -> [“A”, “B”, “C”]

but typically you are just checking for existence or getting all values so having it return a default does not make sense. 

Map
This came out of the ternary and switch discussions, this could be done with map defaults. If we don’t want to add it to the container types that might be a better way to go. See that thread for more details. 



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160116/5b8705dd/attachment.html>


More information about the swift-evolution mailing list