Skip to content

Commit

Permalink
feat(option): add support for variadics
Browse files Browse the repository at this point in the history
* feat(option): add variadic support

* test(option): add tests for option

* docs(option): update options docs for variadics

* feat(option): add join function

* docs(option): add reference for join

* docs(option): add section about joining options
  • Loading branch information
lukadev-0 committed Apr 29, 2024
1 parent 40761b3 commit e47a7d3
Show file tree
Hide file tree
Showing 6 changed files with 642 additions and 247 deletions.
15 changes: 15 additions & 0 deletions .lune/test-lib/randomTuple.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- see .lune/test.luau

export type TupleFunctions = {
map: (...any) -> ...any,
other: () -> ...any,
assertEq: (...any) -> (),
assertEqMapped: (...any) -> (),
assertEqOther: (...any) -> (),
assertEqOtherMapped: (...any) -> (),
}

-- selene: allow(unused_variable)
return function(f: (tuple: TupleFunctions, ...any) -> ())
return nil :: any
end
131 changes: 131 additions & 0 deletions .lune/test.luau
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,137 @@ local function createTestLib(blocks: { TestBlock })
return true
end

local randomTuple = require("./test-lib/randomTuple")
local possibleTypes = { "number", "string", "boolean" }
local alphabet = "abcdefghijklmnopqrstuvwxyz0123456789"

local function createRandomTuple(types: { string }): { any }
local t: { any } = {}
for i, type in types do
if type == "number" then
t[i] = math.random(0, 999)
elseif type == "string" then
local s = ""
for _ = 1, math.random(5, 10) do
local randomChar = math.random(#alphabet)
s ..= alphabet:sub(randomChar, randomChar)
end
t[i] = s
elseif type == "boolean" then
t[i] = math.random() > 0.5
else
error(`Unknown type: {type}`, 2)
end
end
return t
end

local function createAssertEqTuple(name: string, tuple: { any }): (...any) -> ()
local s = ""

s ..= "local "
for i = 1, #tuple do
s ..= `v{i}`
if i < #tuple then
s ..= ", "
end
end
s ..= " = ...\n"

s ..= "if\n"
s ..= ` select("#", ...) ~= {#tuple} or\n`

local tupleStr = ""
for i, v in tuple do
local valueStr = if typeof(v) == "string" then `"{v}"` else `{v}`
s ..= ` v{i} ~= {valueStr}`
tupleStr ..= valueStr

if i < #tuple then
s ..= " or\n"
tupleStr ..= "\t"
end
end
s ..= "\nthen\n"
s ..= ' local s = ""\n'
s ..= ' for i = 1, select("#", ...) do'
s ..= " local v = select(i, ...)\n"
s ..= ' s ..= if typeof(v) == "string" then `"{v}"` else `{v}`\n'
s ..= ' if i < select("#", ...) then\n'
s ..= ' s ..= "\\t"\n'
s ..= " end\n"
s ..= " end\n"
s ..= ` error(\`Equality Failed\\n Expected: \\t{tupleStr}\\n Received: \\t\{s}\`, 2)\n`
s ..= "end"

return luau.load(s, { debugName = name })
end

local function createMapTuple(name: string, types: { string }): (...any) -> ...any
local s = ""

s ..= "local "
for i = 1, #types do
s ..= `v{i}`
if i < #types then
s ..= ", "
end
end
s ..= " = ...\n"

s ..= "return "
for i, type in types do
if type == "string" then
if math.random() > 0.5 then
s ..= `v{i}:upper()`
else
s ..= `v{i}:reverse()`
end
elseif type == "number" then
if math.random() > 0.5 then
s ..= `v{i} + {math.random(5)}`
else
s ..= `v{i} - {math.random(5)}`
end
elseif type == "boolean" then
s ..= `not v{i}`
else
error(`Unknown type: {type}`, 2)
end

if i < #types then
s ..= ", "
end
end

return luau.load(s, { debugName = name })
end

function testLib.randomTuple(f: (randomTuple.TupleFunctions, ...any) -> ())
local types = {}
for i = 1, math.random(10) do
types[i] = possibleTypes[math.random(#possibleTypes)]
end

local originalTuple = createRandomTuple(types)
local otherTuple = createRandomTuple(types)

local map = createMapTuple("map", types)

local tupleFunctions = {
map = map,
other = function()
return unpack(otherTuple)
end,
assertEq = createAssertEqTuple("assertEq", originalTuple),
assertEqMapped = createAssertEqTuple("assertEqMapped", { map(unpack(originalTuple)) }),
assertEqOther = createAssertEqTuple("assertEqOther", otherTuple),
assertEqOtherMapped = createAssertEqTuple("assertEqOtherMapped", { map(unpack(otherTuple)) }),
}

f(tupleFunctions, unpack(originalTuple))
end

return testLib
end

Expand Down
51 changes: 47 additions & 4 deletions docs/docs/optional-values.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,24 @@ To create a `Some` option, we can use `Option.Some`:
local opt = Option.Some(5)
```

An option can contain multiple values:

```lua
local opt = Option.Some(5, "hello", true)
```

To create a `None` option, we can use `Option.None`:

```lua
local opt = Option.None
```

## Accessing the inner value
## Accessing the inner values

There are multiple ways to access the inner value of an option.
There are multiple ways to access the inner values of an option.

The easiest way is to use the `unwrap` method, which will return the inner value
if it exist, or throw an error if it does not:
The easiest way is to use the `unwrap` method, which will return the inner
values if it exist, or throw an error if it does not:

```lua
local some = Option.Some(5)
Expand Down Expand Up @@ -161,6 +167,43 @@ calling them using `:` syntax. [Learn more](/reference/option#typechecking).

:::

## Joining options

Sometimes you have multiple options and you want to combine them into a single
option.

For example, lets say you had two options, and you wanted to get the sum of
their values. You may achieve this using the `andThen` and `map` methods:

```lua
local optA = Option.Some(5)
local optB = Option.Some(10)

local sum = optA:andThen(function(a)
return optB:map(function(b)
return a + b
end)
end)

print(sum) -- Option::Some(15)
```

However, this introduces multiple levels of nesting, which may be undesirable.

Instead, we can use the `join` method to turn both options into a single option,
which we can map over:

```lua
local optA = Option.Some(5)
local optB = Option.Some(10)

local sum = optA:join(optB):map(function(a, b)
return a + b
end)
```

If either option is `None`, the result will be `None`.

## Learn more

There are more methods available on the `Option` type, this was just a small
Expand Down
Loading

0 comments on commit e47a7d3

Please sign in to comment.