witchcrafters / witchcraft Goto Github PK
View Code? Open in Web Editor NEWMonads and other dark magic for Elixir
Home Page: https://witchcrafters.github.io
License: MIT License
Monads and other dark magic for Elixir
Home Page: https://witchcrafters.github.io
License: MIT License
I wasn't able to make sense of the "Properties" section in Witchcraft.Foldable
, but then I don't even know what free theorems are (found this and this on the topic) or what properties Foldable
has. Based on the wording, it also seemed as if this is a general note for maintainers. Am I on the right track?
Properties
People are working on Foldable properties. This is one of the exceptions to there needing to conform to properties. In the meantime, we are testing that naturality is preserved, which is be a free theorm.If that fails, something is very wrong with the instance.
Hey, I'm trying to create a validator like Folktale. However, my spec for concat doesn't compile; I feel like I'm uber close:
defmodule Validator.Success do
alias Validator.Success
defdata any()
@spec new(any()) :: t()
def new(inner), do: %Success{success: inner}
end
defmodule Validator.Failure do
alias Validator.Failure
defdata list()
@spec new(any()) :: t()
def new(inner), do: %Failure{failure: inner}
@spec concat(Witchcraft.Semigroup.t) :: [Witchcraft.Semigroup.t]
def concat(a, b), do: a.failure <> b.failure
end
== Compilation error in file lib/aws/validators.ex ==
** (CompileError) lib/aws/validators.ex:36: spec for undefined function concat/1
(stdlib) lists.erl:1338: :lists.foreach/2
(elixir) lib/kernel/parallel_compiler.ex:121: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1
If I remove the spec concat stuff, it compiles and my unit tests work, so... hopefully it's something small?
Credo now warns by default on use of Apply.apply
, and piping into anonymous functions. We should consider making these not conflict out of the box.
{Credo.Check.Readability.PipeIntoAnonymousFunctions, false},
# This rule incorrectly flags uses of Witchcraft.Semigroupoid.apply/2
{Credo.Check.Refactor.Apply, false}
See #102 (comment)
I wasn't sure where to ask this (here or in TypeClass) but the Witchcraft documentation also briefly mentions how to define instances. What little I know about property testing, I think it is ingenious to use it to ensure type class compliance.
Trying to figure out how to write generators by looking at Witchcraft
and Algae
data types and type class instances, and it seems straightforward, except when it comes to custom generators. For example, Id
, has a generator.ex
,
# lib/algae/id/generator.ex
defimpl TypeClass.Property.Generator, for: Algae.Id do
def generate(_) do
[1, 1.1, "", []]
|> Enum.random()
|> TypeClass.Property.Generator.generate()
|> Algae.Id.new()
end
end
but some instances have custom generators, such as the one for Ord
:
# lib/algae/id/ord.ex
definst Witchcraft.Ord, for: Algae.Id do
custom_generator(_) do
1
|> TypeClass.Property.Generator.generate()
|> Algae.Id.new()
end
def compare(%{id: a}, %{id: b}), do: Witchcraft.Ord.compare(a, b)
end
My first thought was that this is because maybe Ord
does not have instances for all the types in the general generator (i.e., integer, float, string, list), but it does. Although it could be that this wasn't the case when Id
was implemented. Am I on the right track?
Notes to myself, sorry for cluttering up this issue:
PropEr property-based Erlang testing tool
StreamData (same(?) as above in Elixir)
Property-Based Testing with PropEr, Erlang, and Elixir, Fred Hebert's latest book and online guide
Continuation passing style would be a nice addition for many use cases
Main Witchcraft
module should have docs. It's weird having a blank page in the generated docs.
Currently cannot recursively use <-
s in do notation. Pretty sure that this used to work, but a lot of the macros involved have been refactored a few times since then. This limits the use of do notation, though it's still fine for simple cases.
Hi there.
Hopefully I am not mistaking by raising that kind of issue here. Forgive a newcomer please !
I am on macOS 12.0.1, Erlang/OTP 24, Interactive Elixir (1.12.3)
I was willing to experiment with this library. I followed instructions but I get the following message when running %iex -S mix : (trying suggestions did not help).
Many thanks to whoever can help.
JP
== Compilation error in file lib/witchcraft/foldable.ex ==
** (CompileError) lib/witchcraft/foldable.ex:751: function then/2 imported from both Witchcraft.Apply and Kernel, call is ambiguous
(elixir 1.12.3) src/elixir_dispatch.erl:42: :elixir_dispatch.import_function/4
(elixir 1.12.3) src/elixir_fn.erl:76: :elixir_fn.capture_import/4
(stdlib 3.16.1) lists.erl:1358: :lists.mapfoldl/3
(stdlib 3.16.1) lists.erl:1359: :lists.mapfoldl/3
could not compile dependency :witchcraft, "mix compile" failed. You can recompile this dependency with "mix deps.compile witchcraft", update it with "mix deps.update witchcraft" or clean it with "mix deps.clean witchcraft"
Bifunctor
Multifunctor
Contravariant
Plus
Alt
Alternative
MonadPlus
RecursiveChain
Foldable ----> Traversable <--- Functor ------> Alt ---------> Plus Semigroupoid
| | | | |
v v v v v
Foldable1 ---> Traversable1 Apply --------> Applicative -> Alternative Category
| | | |
v v v v
Bind ---------> Monad -------> MonadPlus Arrow
(☝️ from the Haskell semigroupoids lib)
As of Elixir 1.12, ^^^
is deprecated because its precedence will be changing in Elixir 2.0.0. This operator is currently used by Witchcraft.Arrow
, and so we may need to choose another operator, or deprecate its usage here.
Are there any recommended materials on how to use this?
I've tried folktale and it's amazing how much I've improved the codebase with concepts such as Result/Either.
Is http://learnyouahaskell.com/ a good start?
Monoid
Functor
)Applicative
Monad
It would be nice to have Arrows running on top of GenStage where each arrow is producer/consumer.
Such feature allows to use best of functional and erlang worlds.
Arrow
ArrowPlus
Check tha Collectable
is fully equivalent to Foldable
Might want as its own lib
defprotocol
that can accept @property
@property
will run as you defpropimpl
on a datatypeWhen called with first argument being tuple, Functor.map/2
applies a function only to the last element of tuple:
iex(1)> use Witchcraft
iex(2)> map({1, 2, 3}, fn x -> x + 1 end)
{1, 2, 4}
Is this intended behaviour?
What it says in the title
The properties in Orderable
are empty when tests can and probably should be performed (like a == a
and a < b < c && a < c
and others), although really the Orderable.Order
type really needs to be simplified as testing structs is not only slower, it is also unordered when compared to each other. Perhaps -1
, 0
, and 1
should be used as they compare properly as terms as well, plus being significantly faster on the BEAM compared to testing structs (especially as you cannot compare structs/maps with a sensible ordering on the BEAM).
I use Elixir 1.10.2. My module has:
use Witchcraft, except: [>>>: 2]
use Exceptional
but at compilation time I get:
function >>>/2 imported from both Witchcraft.Chain and Exceptional.Raise, call is ambiguous
expanding macro: Witchcraft.Monad.monad/2
If I try to do instead:
use Witchcraft, only: [monad: 2]
use Exceptional
Then I get:
:only and :except can only be given together to import when :only is either :functions or :macros
I am trying to add some error processing to my Timex
usage which is not safe presently. I have copy pasted the code below to get an idea of my use case. It is used in a Phoenix web application.
stream = monad Either.new() do
date1 <- case Map.fetch(params, "date1") do
{:ok, date1_s} -> case Date.from_iso8601(date1_s) do
{:ok, date} -> date |> Timex.to_datetime(:local) |> Either.Right.new
{:error, reason} -> Either.Left.new("error for date1: #{inspect(reason)}")
end
:error -> Timex.now(:local) |> Timex.beginning_of_day |> Either.Right.new
end
# ...
return HydroDB.Database.defaults(site, date1, date2, gens, newdefaults, regex)
end
case stream do
%Either.Right{right: stream_} -> send_stream(conn, stream_, "text/xml")
%Either.Left{left: error} -> send_resp(conn, 422, error)
end
fmap alias <~ is not working:
iex(1)> use Witchcraft
Witchcraft.Comonad
iex(2)> &(&1+1) <~ [1, 2, 3]
#Function<7.126501267/1 in :erl_eval.expr/5>
Hi,
first of all I am pretty excited about finding this library :D its awesome!!!
I was wondering if there is any real world project extensively using witchcraft, to find some practical examples of when it makes sense to use a specific categorie and specifically examples of expressive usages of categories in elixir (e.g. is there an example of code that becomes more expressive by using monads instead of processes).
monad [] do
a <- [1,2,3]
b <- [4,5,6]
return(a + b)
end
#=> [5, 6, 7, 6, 7, 8, 7, 8, 9]
With elixir 1.12 : Apply.then/2
conflicts with Kernel.then/2
in a few places during compilation :
== Compilation error in file lib/witchcraft/foldable.ex ==
** (CompileError) lib/witchcraft/foldable.ex:751: function then/2 imported from both Witchcraft.Apply and Kernel, call is ambiguous
(elixir 1.12.3) src/elixir_dispatch.erl:42: :elixir_dispatch.import_function/4
(elixir 1.12.3) src/elixir_fn.erl:76: :elixir_fn.capture_import/4
(stdlib 3.16.1) lists.erl:1358: :lists.mapfoldl/3
(stdlib 3.16.1) lists.erl:1359: :lists.mapfoldl/3
== Compilation error in file test/witchcraft_test.exs ==
** (CompileError) (for doctest at) lib/witchcraft/apply.ex:382: function then/2 imported from both Witchcraft.Apply and Kernel, call is ambiguous
(elixir 1.12.3) expanding macro: Kernel.|>/2
(for doctest at) lib/witchcraft/apply.ex:382: WitchcraftTest."doctest Witchcraft.Apply.then/2 (36)"/1
(elixir 1.12.3) lib/kernel/parallel_compiler.ex:428: Kernel.ParallelCompiler.require_file/2
(elixir 1.12.3) lib/kernel/parallel_compiler.ex:321: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/7
I am not sure what should be the proper fix, I'll try something quick...
Check that Enumerable
is equivalent to Data.Traversable
. Should obey naturality, identity, and composition.
Category
What it says on the can. It feels weird having them in Witchcraft
As far as I see, there are multiple places where second argument of chainer
is used:
witchcraft/lib/witchcraft/chain.ex
Lines 410 to 431 in 73e3cc6
Instead >>>
(Witchcraft.Chain.chain
) is used.
Is it designed to be so, or it is a typo?
PS IMO it should be
def do_notation(input, chainer) do
input
|> normalize()
|> Enum.reverse()
|> Witchcraft.Foldable.left_fold(fn
continue, {:let, _, [{:=, _, [assign, value]}]} ->
quote do: unquote(value) |> (fn unquote(assign) -> unquote(continue) end).()
continue, {:<-, _, [assign, value]} ->
quote do
apply(unquote(chainer), [unquote(value), fn unquote(assign) -> unquote(continue) end])
end
continue, value ->
quote do
apply(unquote(chainer), [unquote(value), fn _ -> unquote(continue) end])
end
end)
end
Given the highly expressive and nondeterministic nature of property checks, a failed property check can be a little difficult to debug. Ideally there would be help in the modules associated with each property (ie, the ability through the shell h/0
function to obtain a description of the tests associated with each property), as well as messages conveying the test that failed in the property check. Something like
property check `associative` failed for Witchcraft.Semigroup:
from
a = generate(data) # 1
b = generate(data) # 200
c = generate(data) # 6
after
left = a |> Semigroup.append(b) |> Semigroup.append(c) # 10
right = Semigroup.append(a, Semigroup.append(b, c)) # 7
we obtained
equal?(left, right) # false
Obviously there are many ways you lay it out, but the idea would be to point the developer in the direction of what their generated data looked like and what the relation was that failed.
Would a feature like this be interesting to you, and would you accept any pull requests towards it?
When trying to import Witchcraft
without some functions that are also defined in Kernel
module, compile error occurs:
iex(1)> use Witchcraft, except: [<>: 2]
** (CompileError) iex:19: invalid :except option for import, <>/2 is duplicated
(stdlib 3.16.1) lists.erl:1267: :lists.foldl/3
(elixir 1.12.3) src/elixir_import.erl:74: :elixir_import.calculate/6
(elixir 1.12.3) src/elixir_import.erl:24: :elixir_import.import/4
(witchcraft 1.0.3) expanding macro: Witchcraft.Semigroup.__using__/1
iex:19: (file)
iex(1)>
After brief investigation it appears that in every __using__
macro like this in new_opts
there is a duplicate of excepted function, which should be removed instead.
defmacro __using__(opts \\ []) do
{:ok, new_opts} =
Keyword.get_and_update(opts, :except, fn except ->
{:ok, [<>: 2] ++ (except || [])}
end)
if Access.get(opts, :override_kernel, true) do
quote do
import Kernel, unquote(new_opts)
import unquote(__MODULE__), unquote(opts)
end
else
quote do: import(unquote(__MODULE__), unquote(new_opts))
end
end
I'd like to write this:
res = naive_run_receive_loop(Resp.new!(p, []), command, timeout)
try do
Port.close(p)
rescue
_ -> :ok
end
res
as
naive_run_receive_loop(Resp.new!(p, []), command, timeout) <* close(p)
# ...
defp close(p) do
try do
Port.close(p)
rescue
_ -> :ok
end
end
But I can't find the necessary operator. Am I missing something or it's currently impossible?
There is some compilation errors while running mix bench
, which are probably caused by typos:
❯ mix bench
Compiling 28 files (.ex)
warning: ^^^ is deprecated. It is typically used as xor but it has the wrong precedence, use Bitwise.bxor/2 instead
lib/witchcraft/arrow.ex:260
warning: ^^^ is deprecated. It is typically used as xor but it has the wrong precedence, use Bitwise.bxor/2 instead
lib/witchcraft/arrow.ex:261
warning: ^^^ is deprecated. It is typically used as xor but it has the wrong precedence, use Bitwise.bxor/2 instead
lib/witchcraft/arrow.ex:343
warning: module Witchcraft.Monad.Proto is not a behaviour (in module Witchcraft.Monad.Proto.Function)
lib/witchcraft/monad.ex:313: Witchcraft.Monad.Proto.Function (module)
warning: module Witchcraft.Monad.Proto is not a behaviour (in module Witchcraft.Monad.Proto.List)
lib/witchcraft/monad.ex:314: Witchcraft.Monad.Proto.List (module)
warning: module Witchcraft.Monad.Proto is not a behaviour (in module Witchcraft.Monad.Proto.Tuple)
lib/witchcraft/monad.ex:316: Witchcraft.Monad.Proto.Tuple (module)
Compiling lib/witchcraft/ord/map.ex (it's taking more than 10s)
Compiling lib/witchcraft/ord/list.ex (it's taking more than 10s)
Compiling lib/witchcraft/comonad.ex (it's taking more than 10s)
Compiling lib/witchcraft/extend.ex (it's taking more than 10s)
Compiling lib/witchcraft/bifunctor.ex (it's taking more than 10s)
Compiling lib/witchcraft/arrow.ex (it's taking more than 10s)
Generated witchcraft app
warning: variable "y" does not exist and is being expanded to "y()", please use parentheses to remove the ambiguity or change the variable name
bench/witchcraft/arrow/function_bench.exs:18: Witchcraft.Arrow.FunctionBench.h/1
warning: variable "z" is unused (if the variable is not meant to be used, prefix it with an underscore)
bench/witchcraft/arrow/function_bench.exs:18: Witchcraft.Arrow.FunctionBench.h/1
warning: function h/1 is unused
bench/witchcraft/arrow/function_bench.exs:18
== Compilation error in file bench/witchcraft/arrow/function_bench.exs ==
** (CompileError) bench/witchcraft/arrow/function_bench.exs:18: undefined function y/0
(elixir 1.12.3) src/elixir_locals.erl:114: anonymous fn/3 in :elixir_locals.ensure_no_undefined_local/3
(stdlib 3.16.1) erl_eval.erl:685: :erl_eval.do_apply/6
What it says in the title
@expede amazing lib. Because I found 'effects' lib from metalabs, and start exploring repos from the same company, I found witchcraft. I would be easier if it was listed in hex.pm, when searching for 'monad' or 'monads'.
Currently, Applicative for tuples duplicates mempty of the first element:
witchcraft/lib/witchcraft/applicative.ex
Line 221 in 75b62d8
Since we're not constrained by parametricity, couldn't we preserve "type" of every element kind of for free, making the usually controversial instance for n-tuples, where n>2 a tad less controversial?
This isn't Haskell: we may be able to write a general lift*
function.
Hello 👋
I have an issue when trying to get into an interactive session with {:witchcraft, "~> 1.0"}
Interactive Elixir (1.8.1)
Erlang/OTP 21 [erts-10.2.5]
It gets stuck after Generated witchcraft app
and my computer starts to behave so badly.
Note. Never used Witchcraft before
If chain/1
is called inside iex
session once and I try to call it the second time, Elixir tells that chain/1
does not exist. use Witchcraft
or use Witchcraft.Chain
fixes it.
Erlang/OTP 24 [erts-12.1.2] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [jit] [dtrace]
Interactive Elixir (1.12.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> use Witchcraft
Witchcraft
iex(2)> chain do
...(2)> [1,2,3]
...(2)> [4,5,6]
...(2)> end
[4, 5, 6, 4, 5, 6, 4, 5, 6]
iex(3)> chain do
...(3)> [1,2,3]
...(3)> [4,5,6]
...(3)> end
** (CompileError) iex:3: undefined function chain/1
iex(3)> use Witchcraft
Witchcraft
iex(4)> chain do
...(4)> [1,2,3]
...(4)> [4,5,6]
...(4)> end
[4, 5, 6, 4, 5, 6, 4, 5, 6]
iex(5)> chain do
...(5)> [1,2,3]
...(5)> [4,5,6]
...(5)> end
** (CompileError) iex:5: undefined function chain/1
iex(5)>
Seems to be somewhere around Chain
or Monad
. Almost certainly long operation in a prop check.
It's more technically accurate, especially as other functor types are in the roadmap
Should be point to something that isn't a 404
😉
Be able to do something like: http://underscore.io/blog/posts/2015/04/14/free-monads-are-simple.html where a GraphQL request requires multiple sources to collect its information for a given request.
Noticed a few areas like https://github.com/expede/witchcraft/blob/master/lib/witchcraft/apply.ex#L59 where right list concatenation is performed, this is extremely slow on the BEAM as it forces the entire list to be recreated on every operation as it is built up, thus giving it exponential time based on list length. This example should probably be an Enum.map/2
instead of a fold (in fact a lot of the operations in Foldable
could be substantially optimized as many are taking exponential or linear time when they could be done much faster).
Perhaps in another lib, but wanted to write the idea down somewhere 😉
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.