Skip to content
This repository has been archived by the owner on Apr 13, 2021. It is now read-only.

Feature Request: Ability to import external content into tut blocks #187

Open
metasim opened this issue Aug 31, 2017 · 6 comments
Open

Feature Request: Ability to import external content into tut blocks #187

metasim opened this issue Aug 31, 2017 · 6 comments

Comments

@metasim
Copy link
Contributor

metasim commented Aug 31, 2017

I have a number of (Spark-based) tutorials that require the same front-matter to be run along with a shutdown routine and the end. I have to copy/paste these blocks of code into each of my tut documents. It would be great if we could have a modifier that indicates that the code should be read from an external file. For example:

```tut:import(baz/MySetup.scala)
``` 

Even better would be the ability to select subsections of the file by line:

```tut:import(baz/MySetup.scala)
2-10
12-15
20
``` 

I started work on implementing this myself, but the code structure is beyond my FP comfort zone, and am not quite sure where to inject the new functionality.

@tpolecat
Copy link
Owner

You can trick sbt into doing this for you, with a task that preprocesses your source and adds the header/footer material. But that's kind of awful to deal with. I like your idea. 👍

@metasim
Copy link
Contributor Author

metasim commented Aug 31, 2017

Your comment on #172 makes me think you were talking about this item. Unlike that one, I'm not comfortable doing this one, as I really haven't grokked the order of operations, state scoping, sequencing etc. with the State and IO monads. Still don't have the right mental model. As I stated above I got this far and then gave up. :-/

@olafurpg
Copy link

@metasim What is the difference between this feature and something like here below?

```tut:silent
val init = baz.Example()
import init._
```

assuming that baz.Example is on your classpath

@metasim
Copy link
Contributor Author

metasim commented Jan 29, 2018

@olafurpg

You're suggested technique is interesting, but doesn't allow for selective tut execution of code fragments interspersed with explanatory Markdown. Still, I might be able to get some mileage from it.

The original impetus was to allow sections from one file to be selectively injected into the tut stream. If one were to use file names and line numbers as the mechanism to do this, it would take the form outlined below. That said, I actually think using line numbers is clumsy and too fragile. Something like Code Externalization in knitr is closer to the mark, where marker comments in the source trigger the inclusion blocks.

Another way of looking at it is finding a balance between where tut is today (Markdown as the record of authority) and literator (Scala source as the record of authority). I'd like Markdown to be the record of authority for the prose, and Scala source as the record of authority on the code fragments.


Given this Scala source:

object Baloney {
  val line2 = "line2"
  val line3 = "line3"
  val line4 = "line4"
  def line5(something: String): Any = {
    something + " is now on line 6"
  }
  line5("line8")
}

...and this Markdown source:

The start

```tut:import(baz/Baloney.scala)
2
```

```tut
line2
```

```tut:fail
line3
```

```tut:import(baz/Baloney.scala)
3
4
```

```tut:import(baz/Baloney.scala)
5-8
```

```tut:book
line5("tut block")
```


The end

I'd expect something like this output (hand evaluating here...):

The start

```scala
scala>  val line2 = "line2"
line2: String = line2
```

```scala
scala> line2
res0: String = line2
```

```scala
scala> line3
:12: error: not found: value line3
       line3
       ^
```

```scala
scala>   val line3 = "line3"
line3: String = line3

scala>   val line4 = "line4"
line4: String = line4
```

```scala
scala>   def line5(something: String): Any = {
     |     something + " is now on line 6"
     |   }
line5: (something: String)Any

scala>   line5("line8")
res2: Any = line8 is now on line 6
```

```scala
line5("tut block")
// res3: Any = tut block is now on line 6
```


The end

@olafurpg
Copy link

I think I see what you mean. Yes, my workaround is insufficient if you want to inline the declarations. However, building on top of line numbers seems fragile and cryptic to read, to me it's not obvious what the following does

```tut:import(baz/Baloney.scala)
2
```

I don't have any good suggestion. One hacky way might be to inline the code manually with passthrough

@ scala.io.Source.fromFile("Example.scala").getLines.drop(1).take(1).mkString
res3: String = "  val line2 = \"2\""

but that won't explicitly annotate all types and runtime values for each computed value.

@metasim
Copy link
Contributor Author

metasim commented Jan 29, 2018

Yeh, I think line numbers is the wrong way to go. Playing off the knitr Code Externalization, I wonder if something like this would be closer to the mark:

Scala:

object Baloney {
  // tut:begin:a
  val line2 = "line2"
  // tut:end
  // tut:begin:b
  val line3 = "line3"
  val line4 = "line4"
  // tut:end
  // tut:begin:c
  def line5(something: String): Any = {
    something + " is now on line 6"
  }
  // tut:end
  line5("line8")
}

Markdown:

```tut:import(baz/Baloney.scala):a
```

```tut:book:import(baz/Baloney.scala):b
```

```tut:silent:import(baz/Baloney.scala):c
```

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants