PropEr first tries to resolve the expression "bitstring(8, 1)" to a generator, but that fails, because bitstring/2 is not a predefined generator, and there's no custom generator with that signature declared inside the module.
PropEr then tries to find a type with that name, but that fails as well: bitstring/2 is not a predifined type (the way to express the same thing in the type language is <<_:8,_:_*1>>), and there's no local type with that signature.
Quick fix: If you're OK with using only unit values of either 8 or 1, then you can use the predefined generator binary(Base) or bitstring(Base) respectively. If you need other unit values, you'll have to write a custom generator.
One might have expected something like the following to work: -type my_bitstring() :: <<_:16, _:_*3>>. prop_foo() -> ?FORALL(B, my_bitstring(), true). But PropEr will currently reject this for unit values other than 1 and 8, because I suck at predicting what features people will want to use, and thought this was a safe one to skip.
On Tue, Sep 11, 2012 at 4:40 PM, Manolis Papadakis <manopa...@gmail.com> wrote: > PropEr first tries to resolve the expression "bitstring(8, 1)" to a > generator, but that fails, because bitstring/2 is not a predefined > generator, and there's no custom generator with that signature declared > inside the module.
But... According to the Book of Wisdom[1], bitstring(B, U) is there. And binary(S) is not. I haven't seen it before.
> PropEr then tries to find a type with that name, but that fails as well: > bitstring/2 is not a predifined type (the way to express the same thing in > the type language is <<_:8,_:_*1>>), and there's no local type with that > signature.
Eh.. That would kinda do, but it raises a syntax error when used in ?FORALL context.
> Quick fix: If you're OK with using only unit values of either 8 or 1, then > you can use the predefined generator binary(Base) or bitstring(Base) > respectively. If you need other unit values, you'll have to write a custom > generator.
> One might have expected something like the following to work: > -type my_bitstring() :: <<_:16, _:_*3>>. > prop_foo() -> ?FORALL(B, my_bitstring(), true). > But PropEr will currently reject this for unit values other than 1 and 8, > because I suck at predicting what features people will want to use, and > thought this was a safe one to skip.
As said, I am only interested in full-byte binaries. So it was indeed safe for this case.
Until 15 minutes ago how PropEr creates type instances was truly magic for me. Now Eureka! For ones that are curious how it works:
x() -> ?FORALL(S, integer(0, 1023), S < 100).
translates to: x() -> proper:forall(S, fun(S) -> integer(0, 1023) end)).
So we have a closure which tries to execute the nonsense (that's what I thought for more than a year).
After a lot of tracing I found out that this closure is executed in context of `proper_types` moduke, which *has* a function integer(Low, High). That's it. That function returnsa a nothing-magic tuple, and is able to proceed.
And now answering my question. binary/1 is defined in proper_types. :) All makes sense now.
> On Tue, Sep 11, 2012 at 4:40 PM, Manolis Papadakis <manopa...@gmail.com> wrote: >> PropEr first tries to resolve the expression "bitstring(8, 1)" to a >> generator, but that fails, because bitstring/2 is not a predefined >> generator, and there's no custom generator with that signature declared >> inside the module.
> But... According to the Book of Wisdom[1], bitstring(B, U) is there.
You're right, that should be there. Thanks for pointing it out. Crap, now I'll have to find where I've put that code.
> And binary(S) is not. I haven't seen it before.
Yeah, there's lots of other stuff on the API, mostly convenience functions and the like. You can find them all on the API documentation page [1].
>> PropEr then tries to find a type with that name, but that fails as well: >> bitstring/2 is not a predifined type (the way to express the same thing in >> the type language is <<_:8,_:_*1>>), and there's no local type with that >> signature.
> Eh.. That would kinda do, but it raises a syntax error when used in > ?FORALL context.
Unfortunately, many expressions that are valid in a type expression context are invalid Erlang terms, so there's no straightforward way to use them in ?FORALLs directly (that's why my example below uses a custom type as a thin wrapper for the binary type expression).
The types-in-place-of-generators feature is definitely a leaky abstraction [2].
>> Quick fix: If you're OK with using only unit values of either 8 or 1, then >> you can use the predefined generator binary(Base) or bitstring(Base) >> respectively. If you need other unit values, you'll have to write a custom >> generator.
> Actually, I want binaries with length 0..1MiB-1:
>> One might have expected something like the following to work: >> -type my_bitstring() :: <<_:16, _:_*3>>. >> prop_foo() -> ?FORALL(B, my_bitstring(), true). >> But PropEr will currently reject this for unit values other than 1 and 8, >> because I suck at predicting what features people will want to use, and >> thought this was a safe one to skip.
> As said, I am only interested in full-byte binaries. So it was indeed > safe for this case.
> Until 15 minutes ago how PropEr creates type instances was truly magic > for me. Now Eureka! For ones that are curious how it works:
> So we have a closure which tries to execute the nonsense (that's what > I thought for more than a year).
> After a lot of tracing I found out that this closure is executed in > context of `proper_types` moduke, which *has* a function integer(Low, > High). That's it. That function returnsa a nothing-magic tuple, and is > able to proceed.
> And now answering my question. binary/1 is defined in proper_types. :) > All makes sense now.