www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Phobos runnable examples - ideas request

reply "nazriel" <spam dzfl.pl> writes:
Greetings.

I would like to finish Phobos runnable examples case. But I need 
help in picking one strategy for implementation details.

Current state of things is rather bad.
Apart from the fact that lots of examples are not valid examples 
per se

(first example in std.algorithm:
     ---
     int[] a = ...;
     static bool greater(int a, int b)
     ---
)

lots of examples have got outdated assertions in the unittest 
blocks etc.

Requesting user to edit code first, add main(){} block or fix 
invalid D code and then run example itself is unacceptable.

Snippets at main dlang.org website work well because all examples 
are valid D code which compiles out of box. While phobos examples 
are just taken out of context.

The options I've gathered so far:
1) Make all examples valid D code by hand. Make JavaScript assume 
that all code examples in Phobos documentation should be wrapped 
in void main() {} blocks. Add default set of includes + the 
module we are on. Explicitly mark examples that are full programs 
(ie. std.concurrency ones) and don't append void main() {} blocks 
to them. Code should be already wrapped in main(){} block after 
clicking Edit button. Append all special cases where default 
stdin and stdargs are needed like it is done on main webpage ( 
http://dlang.org/js/run.js and 
http://dlang.org/js/run-main-website.js )

2) Make JavaScript assume that all code examples in Phobos 
documentation should be wrapped in void main() {} blocks. Create 
subpage with wiki-like database where special cases would be 
added and script could fetch data from it.
For example adding default stdin and stdargs arguments and 
special includes.

3) The old macro approach, with wrapping examples in $(D_RUN) 
macro. Probably won't scale now due to "unittest is example" 
change in DDOC generation

4) Let's just forget about phobos having runnable examples. On 
the other hand I think there aren't much Programming Languages in 
which stdlib documentation has runnable examples. For example Go 
website has dedicated subpage with couple Examples that can be 
edited and then compiled but that's all. Nothing more. Something 
like main dlang.org website now.


Sorry for any English related quirks :p
Looking forward for your opinions on this and any better ideas 
you may have!

Regards,
Damian Ziemba
Mar 27 2013
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-03-27 22:54, nazriel wrote:

 Requesting user to edit code first, add main(){} block or fix invalid D
 code and then run example itself is unacceptable.

RDMD already has a --main flag and DMD will soon too. -- /Jacob Carlborg
Mar 28 2013
next sibling parent nazriel <spam dzfl.pl> writes:
On 03/28/2013 07:18 AM, Jacob Carlborg wrote:
 On 2013-03-27 22:54, nazriel wrote:

 Requesting user to edit code first, add main(){} block or fix invalid D
 code and then run example itself is unacceptable.

RDMD already has a --main flag and DMD will soon too.

Yes, DMD trunk already does that but it doesn't fully address the problem. While in cases like ---- unittest { assert(...); assert(...); } ---- it will work fine, it will fail in such scenarios: ---- writeln("Hello world"); ---- AFAIK -main flag just slaps void main(){} in dummy file. -- Best regards Damian Ziemba
Mar 28 2013
prev sibling parent nazriel <spam dzfl.pl> writes:
On 03/28/2013 07:46 AM, Timothee Cour wrote:
 I think it's too fragile to require examples inside docs to be
 runnable. A better way would be to expose those examples in the code
 (ie outside of comments). That way, they'll be guaranteed to have
 correct syntax, be properly syntax highlighted, and stay in sync with
 code.

 here's a simple possibility:

 ----
 /**
 Here's a comment before
 */
 version(ddocs_example)  runnable unittest{
      assert (rootName("foo") is null);
      assert (rootName("/foo") == "/");
 }
 version(ddocs_example)  notrunnable unittest{
      //do something not meant to be runnable here
      assert (rootName("/foo") == "/");
 }
 /**
 Here's a comment after
 */
 ----

 This way, DDOC can easily extract version(ddocs_example) unittest
 blocks and present them as runnable examples (for the ones marked with
  runnable). dmd's unittesting would run unittest on those.

This sounds good but won't play good with Phobos developers. Wraping examples in DDOC macros in the past was already very controversial for them. -- Best regards Damian Ziemba
Mar 28 2013
prev sibling next sibling parent Timothee Cour <thelastmammoth gmail.com> writes:
I think it's too fragile to require examples inside docs to be
runnable. A better way would be to expose those examples in the code
(ie outside of comments). That way, they'll be guaranteed to have
correct syntax, be properly syntax highlighted, and stay in sync with
code.

here's a simple possibility:

----
/**
Here's a comment before
*/
version(ddocs_example)  runnable unittest{
    assert (rootName("foo") is null);
    assert (rootName("/foo") == "/");
}
version(ddocs_example)  notrunnable unittest{
    //do something not meant to be runnable here
    assert (rootName("/foo") == "/");
}
/**
Here's a comment after
*/
----

This way, DDOC can easily extract version(ddocs_example) unittest
blocks and present them as runnable examples (for the ones marked with
 runnable). dmd's unittesting would run unittest on those.

On Thu, Mar 28, 2013 at 12:18 AM, Jacob Carlborg <doob me.com> wrote:
 On 2013-03-27 22:54, nazriel wrote:

 Requesting user to edit code first, add main(){} block or fix invalid D
 code and then run example itself is unacceptable.

RDMD already has a --main flag and DMD will soon too. -- /Jacob Carlborg

Mar 28 2013
prev sibling next sibling parent reply 1100110 <0b1100110 gmail.com> writes:
On 03/27/2013 04:54 PM, nazriel wrote:
[snip]
 The options I've gathered so far:
 1) Make all examples valid D code by hand. Make JavaScript assume that
 all code examples in Phobos documentation should be wrapped in void
 main() {} blocks. Add default set of includes + the module we are on.
 Explicitly mark examples that are full programs (ie. std.concurrency
 ones) and don't append void main() {} blocks to them. Code should be
 already wrapped in main(){} block after clicking Edit button. Append all
 special cases where default stdin and stdargs are needed like it is done
 on main webpage ( http://dlang.org/js/run.js and
 http://dlang.org/js/run-main-website.js )

 2) Make JavaScript assume that all code examples in Phobos documentation
 should be wrapped in void main() {} blocks. Create subpage with
 wiki-like database where special cases would be added and script could
 fetch data from it.
 For example adding default stdin and stdargs arguments and special
 includes.

 3) The old macro approach, with wrapping examples in $(D_RUN) macro.
 Probably won't scale now due to "unittest is example" change in DDOC
 generation

 4) Let's just forget about phobos having runnable examples. On the other
 hand I think there aren't much Programming Languages in which stdlib
 documentation has runnable examples. For example Go website has
 dedicated subpage with couple Examples that can be edited and then
 compiled but that's all. Nothing more. Something like main dlang.org
 website now.


 Sorry for any English related quirks :p
 Looking forward for your opinions on this and any better ideas you may
 have!

 Regards,
 Damian Ziemba

I'll work on 1. and see if we can't come up with something better! The code snippets are pretty much necessary IMO. They just need to be fixed. ---- int[] a = ...; static bool greater(int a, int b) { return a > b; } sort!(greater)(a); // predicate as alias sort!("a > b")(a); // predicate as string // (no ambiguity with array name) sort(a); // no predicate, "a < b" is implicit ---- So what about something like this then? It's not *great*, but it actually runs now. ---- void main() { import std.algorithm; int[] a = [7,5,6]; bool greater(int a, int b) { return a > b; } // Here are a few different methods, // Please note I'm doing the same work 3 times. sort!(greater)(a); // predicate as alias sort!("a > b")(a); // predicate as string (no ambiguity with array name) sort(a); // no predicate, "a < b" is implicit assert(isSorted(a));// Proof that it works as expected. ---- } I was gonna come up with a short script to grab all the examples, but apparently github is very protective of out code, and I really don't wanna do html... It'd be great if the js stuck the "void main()" and the imports in for us at least.
Mar 28 2013
parent reply nazriel <spam dzfl.pl> writes:
On 03/28/2013 09:53 AM, 1100110 wrote:
 I'll work on 1. and see if we can't come up with something better!

 The code snippets are pretty much necessary IMO.  They just need to be
 fixed.
 ----
 int[] a = ...;
 static bool greater(int a, int b)
 {
      return a > b;
 }
 sort!(greater)(a);  // predicate as alias
 sort!("a > b")(a);  // predicate as string
                      // (no ambiguity with array name)
 sort(a);            // no predicate, "a < b" is implicit
 ----


 So what about something like this then?
 It's not *great*, but it actually runs now.

 ---- void main() {   import std.algorithm;

      int[] a = [7,5,6];

      bool greater(int a, int b)
      {
          return a > b;
      }
                          // Here are a few different methods,
              // Please note I'm doing the same work 3 times.
      sort!(greater)(a);  // predicate as alias
      sort!("a > b")(a);  // predicate as string (no ambiguity with array
 name)
      sort(a);            // no predicate, "a < b" is implicit
      assert(isSorted(a));// Proof that it works as expected.

 ---- }


 I was gonna come up with a short script to grab all the examples, but
 apparently github is very protective of out code, and I really don't
 wanna do html...

 It'd be great if the js stuck the "void main()" and the imports in for
 us at least.

Yeah, option 1) may work. Although in most cases import moduleWeAreIn; isn't enough. So I guess stabbing default pack of imports maybe necessary. We can work out this way: Adding JS hash-map that will map Phobos modules to imports pack. Something like this: imports["std.algorithm"] = "std.algorithm; std.file: write; std.process: shell"; What ya think? It will also allow to resolve symbol conflicts like std.stdio.write vs std.file.write The default view of example could stay in tact (of course validity of code needs to be fixed anyways), but when you Click edit, script would wrap example in void main() {} and append import pack I mentioned above. However I think that option number 2) may be more scalable. Think about like this: By default all examples are parsed and every snippet is wrapped in void main(){}. Script then fetches data for module we are in. Let's say: std.algorithm; What script gets is: - imports table - default set of imports that is appended to every snippet in module. - special cases - snippets marked explicitly may have additional options like: don't append main() block, add additional imports to certain snippet, replace module imports table with fetched ones, add default standard input, add default standard arguments, don't make this snippet runnable etc. Why it's better than option number 1? Maintaining this data set is way much more easy because you don't need to edit java script file by hand, compute md5sum, make all necessary changes and then make pull request that may wait a bit in pull request queue. You just enter (for example) http://paste.dlang.org/examples search for certain example in database, if it doesn't exist you just add it and make all changes you need. Then depending on what way we pick, request is added to queue and moderator accepts it or rejects OR it just works wiki-like, so change is visible after you submit changes. What you think? Anyways, if you have any questions feel free to mail me at nazriel dzfl.pl -- Best regards Damian Ziemba
Mar 28 2013
parent 1100110 <0b1100110 gmail.com> writes:
On 03/28/2013 12:09 PM, nazriel wrote:
 On 03/28/2013 09:53 AM, 1100110 wrote:
 I'll work on 1. and see if we can't come up with something better!

 The code snippets are pretty much necessary IMO. They just need to be
 fixed.
 ----
 int[] a = ...;
 static bool greater(int a, int b)
 {
 return a > b;
 }
 sort!(greater)(a); // predicate as alias
 sort!("a > b")(a); // predicate as string
 // (no ambiguity with array name)
 sort(a); // no predicate, "a < b" is implicit
 ----


 So what about something like this then?
 It's not *great*, but it actually runs now.

 ---- void main() { import std.algorithm;

 int[] a = [7,5,6];

 bool greater(int a, int b)
 {
 return a > b;
 }
 // Here are a few different methods,
 // Please note I'm doing the same work 3 times.
 sort!(greater)(a); // predicate as alias
 sort!("a > b")(a); // predicate as string (no ambiguity with array
 name)
 sort(a); // no predicate, "a < b" is implicit
 assert(isSorted(a));// Proof that it works as expected.

 ---- }


 I was gonna come up with a short script to grab all the examples, but
 apparently github is very protective of out code, and I really don't
 wanna do html...

 It'd be great if the js stuck the "void main()" and the imports in for
 us at least.

Yeah, option 1) may work. Although in most cases import moduleWeAreIn; isn't enough. So I guess stabbing default pack of imports maybe necessary. We can work out this way: Adding JS hash-map that will map Phobos modules to imports pack. Something like this: imports["std.algorithm"] = "std.algorithm; std.file: write; std.process: shell"; What ya think? It will also allow to resolve symbol conflicts like std.stdio.write vs std.file.write The default view of example could stay in tact (of course validity of code needs to be fixed anyways), but when you Click edit, script would wrap example in void main() {} and append import pack I mentioned above. However I think that option number 2) may be more scalable. Think about like this: By default all examples are parsed and every snippet is wrapped in void main(){}. Script then fetches data for module we are in. Let's say: std.algorithm; What script gets is: - imports table - default set of imports that is appended to every snippet in module. - special cases - snippets marked explicitly may have additional options like: don't append main() block, add additional imports to certain snippet, replace module imports table with fetched ones, add default standard input, add default standard arguments, don't make this snippet runnable etc. Why it's better than option number 1? Maintaining this data set is way much more easy because you don't need to edit java script file by hand, compute md5sum, make all necessary changes and then make pull request that may wait a bit in pull request queue. You just enter (for example) http://paste.dlang.org/examples search for certain example in database, if it doesn't exist you just add it and make all changes you need. Then depending on what way we pick, request is added to queue and moderator accepts it or rejects OR it just works wiki-like, so change is visible after you submit changes. What you think? Anyways, if you have any questions feel free to mail me at nazriel dzfl.pl

I think you may have hit upon something with part #2. Stick with the script idea real quick. But make it slightly more generic. How many times have you wished you had a small code example for the immediate thing you want? I was trying to sell D to my friend, and Bam! readf's non-obvious blocking behaviour hit me! So imagine a little script that sits in front of a github repo.(or whatever, the first place to look is API + rosetta code probably.) You give it the name of a module, or a function, or just straight text with what you want. It gives you code that it knows imports *and uses* that code. I can't figure out what's up with readf? I I now have more than enough public domain examples that (hopefullu..) I'll immediately see the problem. So, extending that a little further, what if it's a script that can tell what you're looking for that can make specific offers, such as the official documentation. That would definitely be a worthy project for D-Programming-Language/Tools, IMO. The script could always sit in front of some website like dpaste, but what if it didn't have to? That's one thing that's always bothered me, we aspired to that kind of ease of use(rdmd --man, for example) but it was never *quite* good enough to be impressed by. Or really use, personally. Imagine it. $ rdmd --search std.algorithm cartesianproduct ====================================================================== auto N = sequence!"n"(0); // the range of natural numbers auto N2 = cartesianProduct(N, N); // the range of all pairs of natural numbers // Various arbitrary number pairs can be found // in the range in finite time. assert(canFind(N2, tuple(0, 0))); assert(canFind(N2, tuple(123, 321))); assert(canFind(N2, tuple(11, 35))); assert(canFind(N2, tuple(279, 172))); ====================================================================== More? [Y/n] $ rdmd --search println ======================================================================= /// Read until end of file import std.stdio; int main() { string buf; while ((buf = stdin.readln()) !is null) write(buf); return 0; } ======================================================================== More? [Y/n] Best of both worlds! It should have a full website UI for searching and even editing. Even if rdmd rejects it, that is something that simply must happen!
Mar 28 2013
prev sibling next sibling parent reply Johannes Pfau <nospam example.com> writes:
Am Wed, 27 Mar 2013 22:54:21 +0100
schrieb "nazriel" <spam dzfl.pl>:

 Greetings.
 
 I would like to finish Phobos runnable examples case. But I need 
 help in picking one strategy for implementation details.
 

With the recent unittest-as-example changes I'd say make runnable examples work with those and promote writing examples as unittests. This way you know for sure that examples compile. AFAIK there's currently no way to disambiguate such an example from a handwritten one, but you could file an enhancement request for that. I'm sure Andrej Mitrovic would implement that quickly. (We'd just need a DDOC_CHECKED_EXAMPLE macro to wrap the example).
Mar 28 2013
parent 1100110 <0b1100110 gmail.com> writes:
On 03/28/2013 01:25 PM, Johannes Pfau wrote:
  Greetings.

  I would like to finish Phobos runnable examples case. But I need
  help in picking one strategy for implementation details.



Good point, I forgot about that.
Mar 28 2013
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Mar 28, 2013 at 07:25:07PM +0100, Johannes Pfau wrote:
 Am Wed, 27 Mar 2013 22:54:21 +0100
 schrieb "nazriel" <spam dzfl.pl>:
 
 Greetings.
 
 I would like to finish Phobos runnable examples case. But I need 
 help in picking one strategy for implementation details.
 

With the recent unittest-as-example changes I'd say make runnable examples work with those and promote writing examples as unittests. This way you know for sure that examples compile. AFAIK there's currently no way to disambiguate such an example from a handwritten one, but you could file an enhancement request for that. I'm sure Andrej Mitrovic would implement that quickly. (We'd just need a DDOC_CHECKED_EXAMPLE macro to wrap the example).

The unittest examples are already running; so all we need to do to make it independently compilable is to wrap the code inside a unittest block, insert an empty main, and it should work. Imports may be handled by including code from version(unittest) blocks. I'm not so sure about this, though. IMO, other than the implicit import of the current module, all code examples should always display all imports explicitly so that copy-n-pasting the code into a unittest block will automatically work, no matter what. T -- There are two ways to write error-free programs; only the third one works.
Mar 28 2013
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/27/13 5:54 PM, nazriel wrote:
 Greetings.

 I would like to finish Phobos runnable examples case. But I need help in
 picking one strategy for implementation details.

 Current state of things is rather bad.
 Apart from the fact that lots of examples are not valid examples per se

 (first example in std.algorithm:
 ---
 int[] a = ...;
 static bool greater(int a, int b)
 ---
 )

 lots of examples have got outdated assertions in the unittest blocks etc.

Thanks for doing this. Incorrect examples are a liability. Examples with unspecified portions (ellipsis) are occasionally useful but at best should work if e.g. the ellipsis is eliminated. For TDPL I have a preprocessor that eliminates (from all examples) lines consisting of ellipses and whitespace only before collecting them for compilation.
 Requesting user to edit code first, add main(){} block or fix invalid D
 code and then run example itself is unacceptable.

Agreed.
 Snippets at main dlang.org website work well because all examples are
 valid D code which compiles out of box. While phobos examples are just
 taken out of context.

 The options I've gathered so far:
 1) Make all examples valid D code by hand. Make JavaScript assume that
 all code examples in Phobos documentation should be wrapped in void
 main() {} blocks. Add default set of includes + the module we are on.
 Explicitly mark examples that are full programs (ie. std.concurrency
 ones) and don't append void main() {} blocks to them. Code should be
 already wrapped in main(){} block after clicking Edit button. Append all
 special cases where default stdin and stdargs are needed like it is done
 on main webpage ( http://dlang.org/js/run.js and
 http://dlang.org/js/run-main-website.js )

 2) Make JavaScript assume that all code examples in Phobos documentation
 should be wrapped in void main() {} blocks. Create subpage with
 wiki-like database where special cases would be added and script could
 fetch data from it.
 For example adding default stdin and stdargs arguments and special
 includes.

I kept those that I prefer. How about this 1.5: assume everything should be wrapped in void main(){}, EXCEPT if the first like is "#!/bin/env rdmd", in which case it's considered a complete script. One note - scoped imports inside functions still have issues, I assume you'll discover some bugs. Works? Andrei
Mar 29 2013