paiza.ioでelixir その187

# 概要
paiza.ioでelixirやってみた。
Kernel.SpecialForms使ってみた。

# サンプルコード

```


defmodule User do
  defstruct name: "john", age: 27
end
#%User{}
#%User{} == %{__struct__: User, name: "john", age: 27}
#%User{age: 31}
#=> %{__struct__: User, name: "john", age: 31}
#%User{age: age} = user
#%User{user | age: 28}
#%User{full_name: "john doe"}
#%User{age: age} = user
#%struct_name{} = user
#struct_name #=> User
#%_{} = user

quote do
  %{"a" => :b, c: :d}
end
|> IO.inspect
#{:%{}, [], [{"a", :b}, {:c, :d}]}

fun = &Kernel.is_atom/1
fun.(:atom)
|> IO.inspect
#true
fun.("string")
|> IO.inspect
#false

double = &(&1 * 2)
double.(2)
|> IO.inspect
#4

take_five = &Enum.take(&1, 5)
take_five.(1..10)
|> IO.inspect
#[1, 2, 3, 4, 5]

first_elem = &elem(&1, 0)
first_elem.({0, 1})
|> IO.inspect
#0

fun = &(&1 + &2 + &3)
fun.(1, 2, 3)
|> IO.inspect
#6

fun = &{&1, &2}
fun.(1, 2)
|> IO.inspect
#{1, 2}

fun = &[&1 | &2]
fun.(1, [2, 3])
|> IO.inspect
#[1, 2, 3]

String.downcase("FOO")
|> IO.inspect
#"foo"

(fn n -> n end).(7)
|> IO.inspect
#7

Hello.World
|> IO.inspect
#Hello.World

Kernel.Sample
|> IO.inspect
#Kernel.Sample

Kernel.length([1, 2, 3])
|> IO.inspect
#3

Kernel.+(1, 2)
|> IO.inspect
#3

Kernel."+"(1, 2)
|> IO.inspect
#3

negate = fn n -> 
    -n 
end
negate.(7)
|> IO.inspect
#-7

quote do
  String.downcase("FOO")
end
|> IO.inspect
#{{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}, [], ["FOO"]}

quote do
  negate.(0)
end
|> IO.inspect
#{{:., [], [{:negate, [], __MODULE__}]}, [], [0]}

quote do
  Hello.World
end
|> IO.inspect
#{:__aliases__, [alias: false], [:Hello, :World]}

x = :downcase
quote do
  String.unquote(x)("FOO")
end
|> IO.inspect
#{{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}, [], ["FOO"]}

x = Sample
quote do
  Module.concat(String, unquote(x))
end
|> IO.inspect
#{{:., [], [{:__aliases__, [alias: false], [:Module]}, :concat]}, [], [{:__aliases__, [alias: false], [:String]}, Sample]}

<<1, 2, 3>>
|> IO.inspect
#<<1, 2, 3>>

<<0, "foo">>
|> IO.inspect
#<<0, 102, 111, 111>>

rest = "oo"
<<102, rest::binary>>
|> IO.inspect
#"foo"

<<"foo"::utf16>>
|> IO.inspect
#<<0, 102, 0, 111, 0, 111>>
<<"foo"::utf32>>
|> IO.inspect
#<<0, 0, 0, 102, 0, 0, 0, 111, 0, 0, 0, 111>>

rest = "oo"
|> IO.inspect

#<<102, rest>>

<<102::integer-native, rest::binary>>
|> IO.inspect
<<102::native-integer, rest::binary>>
|> IO.inspect
<<102::unsigned-big-integer, rest::binary>>
|> IO.inspect
<<102::unsigned-big-integer-size(8), rest::binary>>
|> IO.inspect
<<102::unsigned-big-integer-8, rest::binary>>
|> IO.inspect
<<102::8-integer-big-unsigned, rest::binary>>
|> IO.inspect
<<102, rest::binary>>
|> IO.inspect

<<name::binary-size(5), " the ", species::binary>> = <<"Frank the Walrus">>
|> IO.inspect
#"Frank the Walrus"
{name, species}
|> IO.inspect
{"Frank", "Walrus"}
|> IO.inspect

name_size = 5
<<name::binary-size(name_size), " the ", species::binary>> = <<"Frank the Walrus">>
|> IO.inspect
#{name, species}
#{"Frank", "Walrus"}

<<name_size::size(8), name::binary-size(name_size), " the ", species::binary>> = <<5, "Frank the Walrus">>
|> IO.inspect
#{name, species}
#{"Frank", "Walrus"}

#{name_size, <<name::binary-size(name_size), _rest::binary>>} = {5, <<"Frank the Walrus">>}

x = 1
<<x::8>> == <<x::size(8)>>
|> IO.inspect
#true
<<x::8*4>> == <<x::size(8)-unit(4)>>
|> IO.inspect
#true


<<int::integer>> = <<-100>>
|> IO.inspect
#<<156>>
|> IO.inspect
#int
#156
<<int::integer-signed>> = <<-100>>
|> IO.inspect
#<<156>>
#int
#-100

<<-100::signed, _rest::binary>> = <<-100, "foo">>
|> IO.inspect
#<<156, 102, 111, 111>>

<<number::little-integer-size(16)>> = <<0, 1>>
|> IO.inspect
#<<0, 1>>
#number
#256
<<number::big-integer-size(16)>> = <<0, 1>>
|> IO.inspect
#<<0, 1>>
#number
#1

<<"Hello, ", place::binary>> = "Hello, World"
|> IO.inspect
#"Hello, World"
#place
#"World"

defmodule ImageTyper do
  @png_signature <<137::size(8), 80::size(8), 78::size(8), 71::size(8), 13::size(8), 10::size(8), 26::size(8), 10::size(8)>>
  @jpg_signature <<255::size(8), 216::size(8)>>
  def type(<<@png_signature, _rest::binary>>), do: :png
  def type(<<@jpg_signature, _rest::binary>>), do: :jpg
  def type(_), do: :unknown
end
|> IO.inspect

#ERL_COMPILER_OPTIONS=bin_opt_info mix compile

#left = right(macro)

#^var(macro)

x = 1
x = x + 1
|> IO.inspect
#x
#2

x = 1
^x = List.first([1])
|> IO.inspect
#^x = List.first([2])

x = 0
{x, ^x} = {1, 0}
|> IO.inspect
#x
#1

quote do
  Foo.Bar
end
|> IO.inspect
#{:__aliases__, [alias: false], [:Foo, :Bar]}

quote do
  Foo.bar()
end
|> IO.inspect
#{{:., [], [{:__aliases__, [alias: false], [:Foo]}, :bar]}, [], []}

quote do
  1
  2
  3
end
|> IO.inspect
#{:__block__, [], [1, 2, 3]}

quote do
  {1, 2}
end
|> IO.inspect
#{1, 2}

quote do
  {1, 2, 3}
end
|> IO.inspect
#{:{}, [], [1, 2, 3]}

defmodule Math do
  alias MyKeyword, as: Keyword
end

#Keyword.values #=> uses MyKeyword.values
#Elixir.Keyword.values #=> uses Keyword.values

alias Foo.Bar.Baz

alias Foo.Bar.Baz, as: Baz

alias Foo.{Bar, Baz, Biz}

alias Foo.Bar
alias Foo.Baz
alias Foo.Biz

#case File.read(file) do
#  {:ok, contents} when is_binary(contents) ->
#    String.split(contents, "\n")
#  {:error, _reason} ->
#    Logger.warning "could not find #{file}, assuming empty..."
#    []
#end

x = 10

case x do
  0 ->
    "This clause won't match"

  _ ->
    "This clause would match any value (x = #{x})"
end
|> IO.inspect
#=> "This clause would match any value (x = 10)"
value = 9
data = {:ok, 8}
case data do
  {:ok, value} -> value
  :error -> nil
end
value
|> IO.inspect
#=> unbound variable value

value = 7
lucky? = false
case lucky? do
  false -> 
    value = 13
  true -> 
    true
end

value
|> IO.inspect
#=> 7

x = 1

case 10 do
  ^x -> "Won't match"
  _ -> "Will match"
end
|> IO.inspect
#=> "Will match"

#case data do
#  value when value in [:one, :two] ->
#    "#{value} has been matched"
#  :three ->
#    "three has been matched"
#end

cond do
  hd([1, 2, 3]) ->
    "1 is considered as true"
end
|> IO.inspect
#=> "1 is considered as true"

cond do
  1 + 1 == 1 ->
    "This will never match"
  2 * 2 != 4 ->
    "Nor this"
  true ->
    "This will"
end
|> IO.inspect
#=> "This will"

add = fn a, b -> a + b end
add.(1, 2)
|> IO.inspect
#3

negate = fn
  true -> false
  false -> true
end
negate.(false)
|> IO.inspect
#true

for n <- [1, 2, 3, 4], do: n * 2
|> IO.inspect
#[2, 4, 6, 8]

for n <- [1, 2, 3, 4], do: n * 2
|> IO.inspect
#[2, 4, 6, 8]

for x <- [1, 2], y <- [2, 3], do: x * y
|> IO.inspect
#[2, 3, 4, 6]

for n <- [1, 2, 3, 4, 5, 6], rem(n, 2) == 0, do: n
|> IO.inspect
#[2, 4, 6]

users = [user: "john", admin: "meg", guest: "barbara"]
for {type, name} when type != :guest <- users do
  String.upcase(name)
end
|> IO.inspect
#["JOHN", "MEG"]

pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
for <<r::8, g::8, b::8 <- pixels>>, do: {r, g, b}
|> IO.inspect
#[{213, 45, 132}, {64, 76, 32}, {76, 0, 0}, {234, 32, 15}]

for <<c <- " hello world ">>, c != ?\s, into: "", do: <<c>>
|> IO.inspect
#"helloworld"

for line <- IO.stream(), into: IO.stream() do
  String.upcase(line)
end

for x <- [1, 1, 2, 3], uniq: true, do: x * 2
|> IO.inspect
#[2, 4, 6]

for <<x <- "abcabc">>, uniq: true, into: "", do: <<x - 32>>
|> IO.inspect
#"ABC"

letters = for <<x <- "AbCabCABc">>, x in ?a..?z, do: <<x>>
Enum.reduce(letters, %{}, fn x, acc -> Map.update(acc, x, 1, & &1 + 1) end)
|> IO.inspect
#%{"a" => 1, "b" => 2, "c" => 1}

for <<x <- "AbCabCABc">>, x in ?a..?z, reduce: %{} do
  acc -> Map.update(acc, <<x>>, 1, & &1 + 1)
end
|> IO.inspect
#%{"a" => 1, "b" => 2, "c" => 1}

import List
flatten([1, [2], 3])
|> IO.inspect
#[1, 2, 3]

import List

import List, only: :functions
import List, only: :macros

import List, only: [flatten: 1]
import String, except: [split: 2]

import List, only: [flatten: 1, keyfind: 4]
import List, except: [flatten: 1]

import File.Stream, only: [__build__: 3]

defmodule Math do
  def some_function do
    # 1) Disable "if/2" from Kernel
    import Kernel, except: [if: 2]
    # 2) Require the new "if/2" macro from MyMacros
    #import MyMacros
    # 3) Use the new macro
    #if do_something, it_works
  end
end

quote do
  sum(1, 2, 3)
end
|> IO.inspect
#{:sum, [], [1, 2, 3]}

#{:sum, [], [1, 2, 3]}

#:sum         #=> Atoms
#1            #=> Integers
#2.0          #=> Floats
#[1, 2]       #=> Lists
#"strings"    #=> Strings
#{key, value} #=> Tuples with two elements

quote do
  unquote("hello")
end
|> IO.inspect
#"hello"

quote unquote: false do
  unquote("hello")
end
|> IO.inspect
#{:unquote, [], ["hello"]}

defmodule Math0 do
  defmacro squared(x) do
    quote do
      unquote(x) * unquote(x)
    end
  end
end

#import Math0
#IO.puts("Got #{squared(5)}")

#import Math0
my_number = fn ->
  IO.puts("Returning 5")
  5
end
|> IO.inspect
##IO.puts("Got #{squared(my_number.())}")

#squared(my_number.())

my_number.() * my_number.()

defmodule Math do
  defmacro squared(x) do
    quote do
      x = unquote(x)
      x * x
    end
  end
end

defmodule Math do
  defmacro squared(x) do
    quote bind_quoted: [x: x] do
      x * x
    end
  end
end

quote do
  x = unquote(x)
  x * x
end

#import Math
#squared(5)
x

defmodule Hygiene do
  defmacro no_interference() do
    quote do
      a = 1
    end
  end
end

#require Hygiene

a = 10
#Hygiene.no_interference()
a
|> IO.inspect
#=> 10

defmodule NoHygiene do
  defmacro interference do
    quote do
      var!(a) = 1
    end
  end
end

#require NoHygiene

a = 10
#NoHygiene.interference()
a
|> IO.inspect

#=> 1

defmodule Hygiene do
  defmacro write do
    quote do
      a = 1
    end
  end
  defmacro read do
    quote do
      a
    end
  end
end

#Hygiene.write()
#Hygiene.read()

defmodule ContextHygiene do
  defmacro write do
    quote do
      var!(a, ContextHygiene) = 1
    end
  end
  defmacro read do
    quote do
      var!(a, ContextHygiene)
    end
  end
end

#ContextHygiene.write()
#ContextHygiene.read()
#=> 1

defmodule Hygiene do
  alias Map, as: M
  defmacro no_interference do
    quote do
      M.new()
    end
  end
end

#require Hygiene
#Hygiene.no_interference()
#=> %{}

defmodule Hygiene do
  alias Map, as: M
  defmacro no_interference do
    quote do
      M.new()
    end
  end
end

#require Hygiene
alias SomethingElse, as: M
#Hygiene.no_interference()
#=> %{}

defmodule Hygiene do
  # This will expand to Elixir.Nested.hello()
  defmacro no_interference do
    quote do
      Nested.hello()
    end
  end
  # This will expand to Nested.hello() for
  # whatever is Nested in the caller
  defmacro interference do
    quote do
      alias!(Nested).hello()
    end
  end
end

defmodule Parent do
  defmodule Nested do
    def hello, do: "world"
  end
  require Hygiene
  #Hygiene.no_interference()
  #Hygiene.interference()
  #=> "world"
end

defmodule Hygiene do
  defmacrop get_length do
    quote do
      #length([1, 2, 3])
    end
  end
  def return_length do
    import Kernel, except: [length: 1]
    get_length
  end
end

Hygiene.return_length()
#=> 3

#def return_length do
#  import String, only: [length: 1]
#  get_length
#end

defmodule Lazy do
  defmacrop get_length do
    import Kernel, except: [length: 1]
    quote do
      length("hello")
    end
  end
  def return_length do
    import Kernel, except: [length: 1]
    import String, only: [length: 1]
    get_length
  end
end

#Lazy.return_length()
#|> IO.inspect

#=> 5

# adder.ex
defmodule Adder do
  @doc "Defines a function that adds two numbers"
  defmacro defadd do
    quote location: :keep do
      def add(a, b), do: a + b
    end
  end
end

# sample.ex
defmodule Sample do
  import Adder
  defadd
end

#require Sample
#Sample.add(:one, :two)
#|> IO.inspect

kv = [foo: 1, bar: 2]
#Enum.each(kv, fn {k, v} ->
#  def unquote(k)(), do: unquote(v)
#end)

#defmacro defkv(kv) do
#  Enum.map(kv, fn {k, v} ->
#    quote do
#      def unquote(k)(), do: unquote(v)
#    end
#  end)
#end

#defkv [foo: 1, bar: 2]

#kv = [foo: 1, bar: 2]

#defmacro defkv(kv) do
#  quote do
#    Enum.each(unquote(kv), fn {k, v} ->
#      def unquote(k)(), do: unquote(v)
#    end)
#  end
#end

#defmacro defkv(kv) do
#  quote bind_quoted: [kv: kv] do
#    Enum.each(kv, fn {k, v} ->
#      def unquote(k)(), do: unquote(v)
#    end)
#  end
#end


#receive do
#  {:selector, number, name} when is_integer(number) ->
#    name
#  name when is_atom(name) ->
#    name
#  _ ->
#    IO.puts(:stderr, "Unexpected message received")
#end

#receive do
#  {:selector, number, name} when is_integer(number) ->
#    name
#  name when is_atom(name) ->
#    name
#_ ->
#    IO.puts(:stderr, "Unexpected message received")
#after
#  5000 ->
#    IO.puts(:stderr, "No message in 5 seconds")
#end

#defmodule Math do
#  require MyMacros
#  MyMacros.if do_something, it_works
#end


value =
  quote do
    13
  end
quote do
  sum(1, unquote(value), 3)
end
|> IO.inspect
#{:sum, [], [1, 13, 3]}

value = %{foo: :bar}
quote do
  process_map(unquote(Macro.escape(value)))
end
|> IO.inspect
#{:process_map, [], [{:%{}, [], [foo: :bar]}]}

values = [2, 3, 4]
quote do
  sum(1, unquote_splicing(values), 5)
end
|> IO.inspect
#{:sum, [], [1, 2, 3, 4, 5]}

opts = %{width: 10, height: 15}
with {:ok, width} <- Map.fetch(opts, :width), {:ok, height} <- Map.fetch(opts, :height) do
  {:ok, width * height}
end
|> IO.inspect
#{:ok, 150}

opts = %{width: 10}
with {:ok, width} <- Map.fetch(opts, :width), {:ok, height} <- Map.fetch(opts, :height) do
  {:ok, width * height}
end
|> IO.inspect
#:error

users = %{"melany" => "guest", "bob" => :admin}
with {:ok, role} when not is_binary(role) <- Map.fetch(users, "bob") do
  {:ok, to_string(role)}
end
|> IO.inspect
#{:ok, "admin"}

width = nil
opts = %{width: 10, height: 15}
with {:ok, width} <- Map.fetch(opts, :width), double_width = width * 2, {:ok, height} <- Map.fetch(opts, :height) do
  {:ok, double_width * height}
end
|> IO.inspect
{:ok, 300}
width
|> IO.inspect
#nil

#with :foo = :bar, do: :ok

opts = %{width: 10, height: 15}
with({:ok, width} <- Map.fetch(opts, :width), {:ok, height} <- Map.fetch(opts, :height)) do
  {:ok, width * height}
end
|> IO.inspect
{:ok, 150}

opts = %{width: 10}
with {:ok, width} <- Map.fetch(opts, :width), {:ok, height} <- Map.fetch(opts, :height) do
  {:ok, width * height}
else
  :error ->
    {:error, :wrong_data}
  _other_error ->
    :unexpected_error
end
|> IO.inspect
#{:error, :wrong_data}




```

# 実行結果

```
{:%{}, [], [{"a", :b}, {:c, :d}]}
true
false
4
[1, 2, 3, 4, 5]
0
6
{1, 2}
[1, 2, 3]
"foo"
7
Hello.World
Kernel.Sample
3
3
3
-7
{{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}, [], ["FOO"]}
{{:., [], [{:negate, [], Elixir}]}, [], [0]}
{:__aliases__, [alias: false], [:Hello, :World]}
{{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}, [], ["FOO"]}
{{:., [], [{:__aliases__, [alias: false], [:Module]}, :concat]}, [],
 [{:__aliases__, [alias: false], [:String]}, Sample]}
<<1, 2, 3>>
<<0, 102, 111, 111>>
"foo"
<<0, 102, 0, 111, 0, 111>>
<<0, 0, 0, 102, 0, 0, 0, 111, 0, 0, 0, 111>>
"oo"
"foo"
"foo"
"foo"
"foo"
"foo"
"foo"
"foo"
"Frank the Walrus"
{"Frank", "Walrus"}
{"Frank", "Walrus"}
"Frank the Walrus"
<<5, 70, 114, 97, 110, 107, 32, 116, 104, 101, 32, 87, 97, 108, 114, 117, 115>>
<<1>>
<<0, 0, 0, 1>>
<<156>>
<<156>>
<<156>>
<<156, 102, 111, 111>>
<<0, 1>>
<<0, 1>>
"Hello, World"
{:module, ImageTyper,
 <<70, 79, 82, 49, 0, 0, 5, 200, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 157,
   0, 0, 0, 17, 17, 69, 108, 105, 120, 105, 114, 46, 73, 109, 97, 103, 101, 84,
   121, 112, 101, 114, 8, 95, 95, 105, 110, ...>>, {:type, 1}}
2
1
{1, 0}
{:__aliases__, [alias: false], [:Foo, :Bar]}
{{:., [], [{:__aliases__, [alias: false], [:Foo]}, :bar]}, [], []}
{:__block__, [], [1, 2, 3]}
{1, 2}
{:{}, [], [1, 2, 3]}
"This clause would match any value (x = 10)"
9
7
"Will match"
"1 is considered as true"
"This will"
3
true
2
4
6
8
2
4
6
8
2
3
4
6
2
4
6
["JOHN", "MEG"]
{213, 45, 132}
{64, 76, 32}
{76, 0, 0}
{234, 32, 15}
"h"
"e"
"l"
"l"
"o"
"w"
"o"
"r"
"l"
"d"
2
2
4
6
"A"
"B"
"C"
"A"
"B"
"C"
%{"a" => 1, "b" => 2, "c" => 1}
%{"a" => 1, "b" => 2, "c" => 1}
[1, 2, 3]
{:sum, [], [1, 2, 3]}
"hello"
{:unquote, [], ["hello"]}
#Function<15.40727452 in file:Main.exs>
Returning 5
Returning 5
10
10
{:sum, [], [1, 13, 3]}
{:process_map, [], [{:%{}, [], [foo: :bar]}]}
{:sum, [], [1, 2, 3, 4, 5]}
{:ok, 150}
:error
{:ok, "admin"}
{:ok, 300}
nil
{:ok, 150}
{:error, :wrong_data}
```

# 成果物

https://paiza.io/projects/_ulAtcq8QyGwtpCYS51r8g


以上。