witchcrafters / type_class Goto Github PK
View Code? Open in Web Editor NEW(Semi-)principled type classes for Elixir
Home Page: https://hex.pm/packages/type_class
License: MIT License
(Semi-)principled type classes for Elixir
Home Page: https://hex.pm/packages/type_class
License: MIT License
I am implementing a Witchcraft.Functor
, having an unary function as data inside a struct:
defclass Generator extends Witchcraft.Functor
where do
@type gen_fun_t :: (non_neg_integer -> any)
defdata do
gen_fun :: gen_fun_t()
end
end
The Functor
's map
function is implemented as
def map(gen, map_fun) do
fun seed ->
gen.gen_fun.(seed)
|> map_fun.()
end
|> Generator.new()
end
The property testing of Functor
fails because the struct is compared and the functional values are different by definition of Erlang/Elixir (which is the reason why equals
implements specific comparison method).
Is this the case for @force_type_instance true
, is this a bug of equals
(where it could be useful to define a equal
function the type class instance) or am I missing an alternative approach?
What is Any
? I can't find it neither in the code of type_class
nor in Elixir's docs.
I ran the espec test on my setup (elixir 1.11), and, mostly as a way to slowly understand the code, I would like to address one of the warnings I got:
warning: Proto.wrap/2 is undefined (module Proto is not available or is yet to be defined)
spec/type_class_spec.exs:194: TypeClassSpec.Applicative.wrap/2
This refers to this line.
I was wondering what is this supposed to refer to ? Is there something missing, or is this supposed to be an alias for Applicative.Proto.of/1
and the defdelegate
arguments should be fixed ?
Thanks for the hint.
Oddly enough, both generators in the documentations and the generator I've seen in Maybe are ignoring their argument and use Enum.random()
.
This seems to be an anti-pattern in property-based testing because it relies on reproducibility of a random test that caught an error.
I assume that the underlying plumbing somehow sets a global seed for Enum.random()
, perhaps mix test
even does it itself (hence "Randomized with seed NNN" messages at the end of the tests), but in my opinion a clarification is required here.
I ran the espec test on my setup (elixir 1.11), and, mostly as a way to slowly understand the code, I would like to address one of the warnings I got:
warning: module TypeClassSpec.MoreProps.Proto is not a behaviour (in module TypeClassSpec.MoreProps.Proto.Integer)
spec/type_class_spec.exs:82: TypeClassSpec.MoreProps.Proto.Integer (module)
This refers to this line. I also have this warning quite a few times when building witchcraft.
The Adder
class doesn't trigger this warning, so it seems to be because there is no where
section in the definition of MoreProps
class... or because extend
is not working as expected ? I'm guessing it should declare the protocol for this class, from the where
section of the extended module somehow ?
Thanks for any info about this.
Hi everyone, here is some feedback...
After using type_class
for a while, I reached the conclusion that running property tests during compilation doesn't match the "usual elixir developer" expectations. Well, at least mine, I'll let everyone else make their own mind about it while I attempt to make my case.
I see mainly two reasons:
False positives: if mix compile
succeed, it might just be because the values generated didn't break the property, but the property may still be unsatisfied for values that weren't generated. Further mix test
runs will not trigger the property tests again, and therefore will not expose the problem. We now have "broken code" compiled and tests passing...
Breaking the usual code / test cycle: the developer is writing tests & modifying code & writing tests while running mix test
very frequently to see what tests are impacted by the code changes. I see this situation being more common with dynamic languages, where "exploratory coding" is easier, compared to compiled languages, where compile time make that somewhat less fluid...
In this usecase, a developer can modify code and then running mix test
will recompile, and maybe break on compilation (property test failed). Where is the code triggering the problem ? The property might be very remotely related with the changed code, and tracking it down maybe difficult, because now the developer cannot run tests anymore, until the cause for the property failing is found... Usually having multiple tests failing will indicate the code path that was affected by a change, but by running type_class property tests on their own, in the compilation phase, it has become impossible to run any other test to help track down the problem.
What does everyone else think ?
Overall I find type_class very useful for structuring code, but I would prefer typeclass property tests to run with mix test
.
Especially because a codepath can jump through various protocol implementation in various files, having tests failing along the path would help... Maybe via a classtest
macro, accepting the typeclass atom to test as parameter, that could be used with ExUnit
just like doctest
?
I recently had an issue where floats where embedded into a struct, and checking with equal?
on the whole struct didn't round the float (as it does on one float).
I also don't want to have a property defined in a typeclass, with a specific notion of equality that depends on the implementation...
I am thinking that equal?
should be defined in a protocol, and implementable for any struct, just like generator is ?
What do you all think ?
TypeClass.__using__/1
is defined as:
defmacro __using__(_) do
quote do
require unquote(__MODULE__)
import unquote(__MODULE__)
end
end
When a module is imported, the imported macros can be invoked without require
, so the require is not necessary. This leaves import
as the sole member of __using__
, which is considered a bad practice. It's much clearer to the user of the library to say what is happening when they see:
import TypeClass
than, when they see:
use TypeClass
That could be doing anything.
Currently, prop checks fail with minimal context. It would be good to have more context on which specific values were the cause.
This is an error similar to what I encountered while trying to implement a linearspace typeclass on top of Witchcraft.Monoid
.
Here is a minimal example to reproduce the issue:
# -- type_class_check.ex --
#
# HOW TO USE:
# From iex, enter these lines (minus the prompt)
#
# iex> Mix.install([ {:witchcraft, "~> 1.0"}, {:type_class, "~> 1.2"} ])
# :ok
# iex> c "type_class_check.ex"
import TypeClass
defclass SomeClass do
extend Witchcraft.Monoid
alias Witchcraft.Semigroup
where do
def merge(arg1, arg2)
end
properties do
def commutativity(data) do
a = generate(data)
b = generate(data)
Semigroup.append(a, b) == Semigroup.append(b, a)
end
end
end
definst SomeClass, for: Integer do
# custom_generator(data) do
# data |> IO.inspect()
# end
def merge(arg1, arg2) do
arg1 * arg2
end
end
This should compile without problem, as expected.
However uncommenting the custom generator will reveal all the different sort of data that is passed to it. It doesn't match the current implementation (Integer) as I would have expected...
&:erlang.is_number/1
%{
-262 => %{-239 => <<23, 44, 40, 86, 8, 28, 19, 9, 23, 51, 52, 15>>},
618 => -1.0225806451612902,
"\eSJ)A,>PS" => 3.297872340425532,
<<82, 23, 77, 79, 72, 26, 88, 5, 46, 33, 8, 6, 79, 14, 68, 23, 74, 81, 31, 6>> => "6"
}
== Compilation error in file type_class_check.ex ==
** (FunctionClauseError) no function clause matching in Witchcraft.Semigroup.Proto.Function.append/2
The following arguments were given to Witchcraft.Semigroup.Proto.Function.append/2:
# 1
&:erlang.is_number/1
# 2
%{-262 => %{-239 => <<23, 44, 40, 86, 8, 28, 19, 9, 23, 51, 52, 15>>}, 618 => -1.0225806451612902, "\eSJ)A,>PS" => 3.297872340425532, <<82, 23, 77, 79, 72, 26, 88, 5, 46, 33, 8, 6, 79, 14, 68, 23, 74, 81, 31, 6>> => "6"}
Attempted function clauses (showing 1 out of 1):
def append(f, g) when is_function(g)
(witchcraft 1.0.4) lib/witchcraft/semigroup.ex:120: Witchcraft.Semigroup.Proto.Function.append/2
type_class_check.ex:23: SomeClass.Property.commutativity/1
(type_class 1.2.8) lib/type_class/property.ex:32: anonymous fn/5 in TypeClass.Property.run!/4
(elixir 1.14.3) lib/stream.ex:1557: Stream.do_repeatedly/3
(elixir 1.14.3) lib/enum.ex:3448: Enum.take/2
type_class_check.ex:37: anonymous fn/2 in :elixir_compiler_6.__FILE__/1
(elixir 1.14.3) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
type_class_check.ex:37: (file)
** (CompileError) compile error
(iex 1.14.3) lib/iex/helpers.ex:204: IEx.Helpers.c/2
iex:3: (file)
I was actually trying this as a simple workaround for #19 ...
Now I am guessing the error is related to this package and the design of custom_generator, but I am not used to type_class
, so feel free to let me know if I am just holding this wrong, or if I should post this somewhere else (witchcraft itself ?).
Cheers !
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.