Giter VIP home page Giter VIP logo

witchcraft's People

Contributors

arecvlohe avatar baseballlover723 avatar capitalist avatar crowdhailer avatar expede avatar florius0 avatar icidasset avatar jechol avatar overminddl1 avatar paulstatezny avatar quinnwilton avatar stefkin avatar thetamind avatar thth avatar toraritte avatar trevoke avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

witchcraft's Issues

Is the "Properties" section in `Witchcraft.Foldable` documentation a note for maintainers?

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.

Validation concat method

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?

Bring in line with latest community standards

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)

[question] When to write custom generators for type class instances?

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:

Build in some CPS

Continuation passing style would be a nice addition for many use cases

Top-level docs

Main Witchcraft module should have docs. It's weird having a blank page in the generated docs.

Fix recursive `<-` s in do notation

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.

unable to use witchcraft

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"

Add uncommon functors

  • Bifunctor
  • Multifunctor
  • Contravariant
  • Comonad
  • 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)


(☝️ from the the Fantasy Land spec)

Address ^^^ deprecation

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.

elixir-lang/elixir#11590

How to use?

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?

Complete basics

  • Monoid
  • Functors
    • Common
      • Covariant Functor (just Functor)
      • Applicative
      • Monad

Set up CI

  • Link CircleCI
  • Configure to actually run tests
  • Ensure that existing tests pass

Add GenStage support for arrows

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.

defproprotocol

Might want as its own lib

  • Define a macro wrapper around defprotocol that can accept @property
    • @property will run as you defpropimpl on a datatype
    • Perhaps make it possible to point at a file, or maybe just import function(s) to run
    • Check an env flag for disable, so that it doesn't have to run every compile if you're iterating and whatnot

Functor.map behaviour on tuples

When 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?

Orderable properties are empty

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).

How to use Witchcraft and Exceptional in the same module ?

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

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>

Example Project that uses witchcraft extensively

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).

"Do" notation

monad [] do
  a <- [1,2,3]
  b <- [4,5,6]
  return(a + b)
end
#=> [5, 6, 7, 6, 7, 8, 7, 8, 9]

elixir 1.12 : then/2 conflicts with Kernel.then/2

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...

`Witchcraft.Chain.do_notation/2` intentionally ignores `chainer`

As far as I see, there are multiple places where second argument of chainer is used:

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
import Witchcraft.Chain, only: [>>>: 2]
unquote(value) >>> fn unquote(assign) -> unquote(continue) end
end
continue, value ->
quote do
import Witchcraft.Chain, only: [>>>: 2]
unquote(value) >>> fn _ -> unquote(continue) end
end
end)
end

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

More informative FailedCheckError messages

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?

`use Witchcraft` fails when excepting functions defined in `Kernel`

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

Right and left sparrows

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?

Benchmarks compilation error

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

Curry the `lift`s

This isn't Haskell: we may be able to write a general lift* function.

Unable to use Witchcraft, iex (1.8.1)

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]

See recording

It gets stuck after Generated witchcraft app and my computer starts to behave so badly.

Note. Never used Witchcraft before

`chain/1` disappears from iex

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)>

Slow compilation

Seems to be somewhere around Chain or Monad. Almost certainly long operation in a prop check.

Exponentially slow list operations

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).

Lenses

Perhaps in another lib, but wanted to write the idea down somewhere 😉

Dialyzer and .Proto modules

Hi, I'm taking a look at this project as a means to learn more about the topic.
I thought about running dialyzer to see what it would find.

Why are dialyzer and the compiler so unhappy about these .Proto modules?

image

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.