-
-
Notifications
You must be signed in to change notification settings - Fork 666
sorting-room
: Implement exercise to teach about type-conversion
and type-assertion
#1723
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
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
7a3484a
Setup initial `sorting-room` exercise
jmrunkle e002c48
Finish the tests, intro, and hints
jmrunkle ad08be5
gofmt the test file
jmrunkle ef5b7e4
Remove type-conversion from `cars-assemble`
jmrunkle 0ea607a
Mention type switch and string-formatting prereq
jmrunkle 5d610bf
Remove booleans from `sorting-room`
jmrunkle 893f7b3
Add FancyNumberBox to `sorting-room`
jmrunkle 740397a
Copy introduction materials from the concepts
jmrunkle 515a700
Mark as wip until interfaces is taught
jmrunkle 5c55e2d
Fix differentFancyNumber declaration
jmrunkle 88a7044
Switch FancyNumber to FancyBox
jmrunkle 9ac3cd9
Fix import and parameter name
jmrunkle 8144f25
Apply recommendation for the instructions
jmrunkle 316a398
Add junedev as a contributor in config.json
jmrunkle File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Hints | ||
|
||
## 1. Describe simple numbers | ||
|
||
- use `fmt.Sprintf` to format | ||
|
||
## 2. Describe a number box | ||
|
||
- get the result from the `Number()` method | ||
- use `fmt.Sprintf` to format | ||
|
||
## 3. Implement a method extracting the number from a fancy number box | ||
|
||
- use a type assertion to check if this is a `FancyBox` | ||
- get the `string` from the `Value()` method | ||
- use `strconv.Atoi` to convert the `string` to an `int` (we can throw away the error since we want 0 if it cannot be converted to an `int`) | ||
|
||
## 4. Describe a fancy number box | ||
|
||
- use `ExtractFancyNumber` to get the `int` value | ||
- convert the `int` to a `float64` | ||
- use `fmt.Sprintf` to format | ||
|
||
## 5. Implement `DescribeAnything` which uses them all | ||
|
||
- either use type assertions (eg. `i.(someType)`) or a type switch (`switch v := i.(type)`) | ||
- remember to convert `int` to a `float64` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# Instructions | ||
|
||
Jen is working in the sorting room in a large factory. | ||
The sorting room needs to process anything that comes into it by categorizing it with a label. | ||
Jen is responsible for things that were pre-categorized as numbers and needs a program to help her with the sorting. | ||
|
||
Most primitive values should get straight-forward labels. | ||
For numbers, she wants strings saying `"This is the number 2.0"` (if the number was 2). | ||
Jen wants the same output for integers and floats. | ||
|
||
There are a few `Box` interfaces that need to be unwrapped to get their contents. | ||
For a `NumberBox`, she wants strings saying `"This is a box containing the number 3.0"` (if the `Number()` method returns 3). | ||
For a `FancyNumberBox`, she wants strings saying `"This is a fancy box containing the number 4.0"`, but only if the type is a `FancyNumber`. | ||
|
||
Anything unexpected should say `"Return to sender"` so Jen can send them back where they came from. | ||
|
||
## 1. Describe simple numbers | ||
|
||
Jen wants numbers to return strings like `"This is the number 2.0"` (including one digit after the decimal): | ||
|
||
```go | ||
fmt.Println(DescribeNumber(-12.345)) | ||
// Output: This is the number -12.3 | ||
``` | ||
|
||
## 2. Describe a number box | ||
|
||
Jen wants numbers to return strings like `"This is a box containing the number 2.0"` (again, including one digit after the decimal): | ||
|
||
```go | ||
fmt.Println(DescribeNumberBox(numberBoxContaining{12.345})) | ||
// Output: This is a box containing the number 12.3 | ||
``` | ||
|
||
## 3. Implement a method extracting the number from a fancy number box | ||
|
||
Jen needs a helper function to extract the number from a `FancyNumberBox`. | ||
If the `FancyNumberBox` is a `FancyNumber`, extract its value and convert it from a `string` to an `int`. | ||
Any other type of `FancyNumberBox` should return 0. | ||
|
||
```go | ||
fmt.Println(ExtractFancyNumber(FancyNumber{"10"})) | ||
// Output: 10 | ||
fmt.Println(ExtractFancyNumber(AnotherFancyNumber{"one"})) | ||
// Output: 0 | ||
``` | ||
|
||
## 4. Describe a fancy number box | ||
|
||
If the `FancyNumberBox` is a `FancyNumber`, Jen wants strings saying `"This is a fancy box containing the number 4.0"`. | ||
Any other type of `FancyNumberBox` should say `"This is a fancy box containing the number 0.0"`. | ||
|
||
```go | ||
fmt.Println(DescribeFancyNumberBox(FancyNumber{"10"})) | ||
// Output: This is a fancy box containing the number 10.0 | ||
fmt.Println(DescribeFancyNumberBox(AnotherFancyNumber{"one"})) | ||
// Output: This is a fancy box containing the number 0.0 | ||
``` | ||
|
||
NOTE: we should use the `ExtractFancyNumber` function! | ||
|
||
## 5. Implement `DescribeAnything` which uses them all | ||
|
||
This is the main function Jen needs which takes any input (the empty interface means any value at all: `interface{}`). | ||
`DescribeAnything` should delegate to the other functions based on the type of the value passed in. | ||
More specifically: | ||
|
||
- `int` and `float64` should both delegate to `DescribeNumber` | ||
- `NumberBox` should delegate to `DescribeNumberBox` | ||
- `FancyNumberBox` should delegate to `DescribeFancyNumberBox` | ||
- anything else should result in `"Return to sender"` | ||
|
||
```go | ||
fmt.Println(DescribeAnything(numberBoxContaining{12.345})) | ||
// Output: This is a box containing the number 12.3 | ||
fmt.Println(DescribeAnything("some string")) | ||
// Output: Return to sender | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
# Introduction | ||
|
||
## Type Conversion | ||
|
||
Go requires explicit conversion between different types. | ||
Converting between types (also known as **type casting**) is done via a function with the name of the type to convert to. | ||
For example, to convert an `int` to a `float64` you would need to do the following: | ||
|
||
```go | ||
var x int = 42 // x has type int | ||
f := float64(x) // f has type float64 (ie. 42.0) | ||
``` | ||
|
||
## Converting between primitive types and strings | ||
|
||
There is a `strconv` package for converting between primitive types (like `int`) and `string`. | ||
|
||
```go | ||
import "strconv" | ||
|
||
var intString string = "42" | ||
var i int = strconv.Atoi(intString) | ||
|
||
var number int = 12 | ||
var s string = strconv.Itoa(number) | ||
``` | ||
|
||
## Type Assertions | ||
|
||
Interfaces in Go can introduce ambiguity about the underlying type. | ||
A type assertion allows us to extract the interface value's underlying concrete value using this syntax: `interfaceVariable.(concreteType)`. | ||
|
||
For example: | ||
|
||
```go | ||
var input interface{} = 12 | ||
number := input.(int) | ||
``` | ||
|
||
NOTE: this will cause a panic if the interface variable does not hold a value of the concrete type. | ||
|
||
We can test whether an interface value holds a specific concrete type by making use of both return values of the type assertion: the underlying value and a boolean value that reports whether the assertion succeeded. | ||
For example: | ||
|
||
```go | ||
str, ok := input.(string) // no panic if input is not a string | ||
``` | ||
|
||
If `input` holds a `string`, then `str` will be the underlying value and `ok` will be true. | ||
If `input` does not hold a `string`, then `str` will be the zero value of type `string` (ie. `""` - the empty string) and `ok` will be false. | ||
No panic occurs in any case. | ||
|
||
## Type Switches | ||
|
||
A **type switch** can perform several type assertions in series. | ||
It has the same syntax as a type assertion (`interfaceVariable.(concreteType)`), but the `concreteType` is replaced with the keyword `type`. | ||
Here is an example: | ||
|
||
```go | ||
switch v := i.(type) { | ||
case int: | ||
fmt.Println("the integer %d", v) | ||
case string: | ||
fmt.Println("the string %s", v) | ||
default: | ||
fmt.Println("some type we did not handle explicitly") | ||
} | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"blurb": "Learn about type casting and type assertions in the sorting room.", | ||
"authors": [ | ||
"jmrunkle" | ||
], | ||
jmrunkle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"contributors": [ | ||
"junedev" | ||
], | ||
"files": { | ||
"solution": [ | ||
"sorting_room.go" | ||
], | ||
"test": [ | ||
"sorting_room_test.go" | ||
], | ||
"exemplar": [ | ||
".meta/exemplar.go" | ||
] | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Design | ||
|
||
## Learning objectives | ||
|
||
- How to convert from one type to another | ||
- How to assert a type | ||
- How to assert an interface type | ||
jmrunkle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- How to use a type switch | ||
|
||
## Out of scope | ||
|
||
- Defining new types | ||
|
||
## Concepts | ||
|
||
- `type-conversion`: how to convert between types | ||
- `type-assertion`: how to assert an interface type has a particular concrete type | ||
|
||
## Prerequisites | ||
|
||
This exercise's prerequisites Concepts are: | ||
|
||
- `interfaces`: what is an interface and how to use them | ||
- `string-formatting`: how to format strings |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package sorting | ||
jmrunkle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
) | ||
|
||
// DescribeNumber should return a string describing the number. | ||
func DescribeNumber(f float64) string { | ||
return fmt.Sprintf("This is the number %.1f", f) | ||
} | ||
|
||
type NumberBox interface { | ||
Number() int | ||
} | ||
|
||
// DescribeNumberBox should return a string describing the NumberBox. | ||
func DescribeNumberBox(nb NumberBox) string { | ||
return fmt.Sprintf("This is a box containing the number %.1f", float64(nb.Number())) | ||
} | ||
|
||
// FancyNumber holds an integer as a string | ||
type FancyNumber struct { | ||
n string | ||
} | ||
|
||
func (i FancyNumber) Value() string { | ||
return i.n | ||
} | ||
|
||
type FancyNumberBox interface { | ||
Value() string | ||
} | ||
|
||
// ExtractFancyNumber should return the integer value for a FancyNumber | ||
// or 0 if any other type of FancyNumberBox is supplied. | ||
func ExtractFancyNumber(fnb FancyNumberBox) int { | ||
fancyNum, ok := fnb.(FancyNumber) | ||
if !ok { | ||
return 0 | ||
} | ||
|
||
num, _ := strconv.Atoi(fancyNum.Value()) | ||
return num | ||
} | ||
|
||
// DescribeFancyNumberBox should return a string describing the FancyNumberBox. | ||
func DescribeFancyNumberBox(fnb FancyNumberBox) string { | ||
return fmt.Sprintf( | ||
"This is a fancy box containing the number %.1f", | ||
float64(ExtractFancyNumber(fnb))) | ||
} | ||
|
||
// DescribeAnything should return a string describing whatever it contains. | ||
func DescribeAnything(i interface{}) string { | ||
jmrunkle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
switch v := i.(type) { | ||
case int: | ||
return DescribeNumber(float64(v)) | ||
case float64: | ||
return DescribeNumber(v) | ||
case NumberBox: | ||
return DescribeNumberBox(v) | ||
case FancyNumberBox: | ||
return DescribeFancyNumberBox(v) | ||
default: | ||
return "Return to sender" | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module sorting | ||
|
||
go 1.14 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package sorting | ||
|
||
// DescribeNumber should return a string describing the number. | ||
func DescribeNumber(f float64) string { | ||
panic("Please implement DescribeNumber") | ||
} | ||
|
||
type NumberBox interface { | ||
Number() int | ||
} | ||
|
||
// DescribeNumberBox should return a string describing the NumberBox. | ||
func DescribeNumberBox(nb NumberBox) string { | ||
panic("Please implement DescribeNumberBox") | ||
} | ||
|
||
type FancyNumber struct { | ||
n string | ||
} | ||
|
||
func (i FancyNumber) Value() string { | ||
return i.n | ||
} | ||
|
||
type FancyNumberBox interface { | ||
Value() string | ||
} | ||
|
||
// ExtractFancyNumber should return the integer value for a FancyNumber | ||
// and 0 if any other FancyNumberBox is supplied. | ||
func ExtractFancyNumber(fnb FancyNumberBox) int { | ||
panic("Please implement ExtractFancyNumber") | ||
} | ||
|
||
// DescribeFancyNumberBox should return a string describing the FancyNumberBox. | ||
func DescribeFancyNumberBox(fnb FancyNumberBox) string { | ||
panic("Please implement DescribeFancyNumberBox") | ||
} | ||
|
||
// DescribeAnything should return a string describing whatever it contains. | ||
func DescribeAnything(i interface{}) string { | ||
panic("Please implement DescribeAnything") | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.