View Source Basics
This guide will show the basics of Pathex
. This is the best place to start learning Pathex
.
It won't take more than 5 minutes.
As README says, Pathex is a library for working with collections and nested key-value structures. Pathex uses powerful functional abstraction called lens (in Pathex we call it path or path-closure, you'll later see why). Path is a basically a path to value in nested structure, almost like filesystem path.
create-paths
Create paths
First of all, we need to know how to create paths. Path can be created with Pathex.path/2
macro.
For example:
p = path :x / :y
# Note the "/". That's why the library is called Pathex
This path specifies a way to get (or set, or delete or whatever) value 1
from %{x: %{y: 1}}
Note: You don't have to capture used variables
x = 1 path :some_atom / x # Right path :some_atom / ^x # Wrong
Each type of value used in path
corresponds to one or more types of Elixir collections
- Strings, tuples, maps, functions, lists are treated only as
Map
keys. - Atoms are treated as
Map
orKeyword
keys. - Positive integers are treated as
Map
keys orList
andTuple
indexes - Negative integers are treated as
Map
keys or first element inList
's view or prepending element inList
'supdate
orforce_set
operations - Variables are treated the same way as the type they have in runtime. For example, variables containing negative
integers are treated as negative integer values. Unfortunately, it is not possible to know variable type at compile-time,
that's why
Pathex
generates code for all possible types. See performance tips for more information.
use-paths
Use paths
Alright, we have a path. Now, we need to know how to use it
Let's take a value from nested structure
iex> p = path :usernames / 0
iex> Pathex.view(%{usernames: ["SubZero", "Scorpion"]}, p)
{:ok, "SubZero"}
There are a lot of other ways to use paths. Every macro with human-readable name
(except path
) is a macro for using path in some way, check Pathex
documentation.
In all such macros first argument is an input structure and the second argument is a path-closure itself
Note: Path-closure which are created inside path-using macro are optimised to have only one operation generated (instead of default three). You can read more about path-closures and operations here
As usual
- Macro without
!
always return{:ok, result}
or:error
- Macro with
!
returnresult
or raisePathex.Error
compose-paths
Compose paths
Path-closures can be composed together to create new path-closure, every path comosition macro is a binary operator. Some compositions are optimized to have generate one closure even if multiple closures are used.
You can concat paths with Pathex.~>/2
composition macro
iex> p1 = path :x
iex> p2 = path :y
iex> composed_path = p1 ~> p2
iex> 1 = Pathex.view(%{x: [y: 1]}, composed_path)
Think about paths composition just like paths concatenation in shell
For exampleiex> first_user = path :users / 0 # users/0 iex> name = path :name # name iex> first_user_name = first_user ~> name # users/0/name
prebuilt-paths
Prebuilt paths
Pathex
provides some prebuilt paths for non-standart data manipulation. You
can find them in Pathex.Lenses
module. You can read more about them in
lenses guide
For example Pathex.Lenses.star/0
lens works just like *
in filesystem path.
performance-tips
Performance tips
General rule: the more data you provide to Pathex
at compile-time the better
For example:
path(1 / :x / :y, :json)
# works faster than
path(1 / :x / :y)
Because :json
mod optimizes closure to one big case,
while default :naive
mod generates nested cases
And, in this example:
path(1 / :x / :y)
# works faster than
x = :x; path(1 / x / :y)
Because constants provide more information about available type.
This means that for :x
pathex know that this can be a to Keyword
or Map
,
while x
means that this is a variable which can contain List
/Tuple
index or Map
/Keyword
value
And, in this example:
path(1 / x / :y)
# works faster than
path(1) ~> path(x) ~> path(:y)
Because paths concatenation actually creates a path which calls all operands internally which increases the call stack and makes concatenated path handle errors by hand