off-topic

https://github.com/clojurians/community-development/blob/master/Code-of-Conduct.md Clojurians Slack Community Code of Conduct. Searchable message archives are at https://clojurians-log.clojureverse.org/
2021-06-06T14:21:59.297Z

If anyone wants to join today at 10:30 am MDT, I’ll be hosting a discussion group for two pieces by Brett Victor, Learnable Programming and Up and Down the Ladder of Abstraction. https://www.guidedtrack.com/programs/og010mb/run

❤️ 2
2021-06-06T21:33:50.298600Z

If someone were to visualize data flow through time for Clojure, how would it do that differently than Factorio or Opus Magnum? https://www.youtube.com/watch?v=U-aXBcJfxM0

Ben Sless 2021-06-07T07:25:04.300300Z

Not sure I 100% understand your question, but you bring up an interesting subject. Whenever I build systems with queues I feel like I'm playing factorio Is this question related to your previous posts regarding visualization?

2021-06-07T12:46:59.302Z

Yeah. Factorio and Opus Magnum are models of imperative programming, so I didn’t know what it would look like if it were functional

Ben Sless 2021-06-07T12:57:35.302300Z

I'm not sure I'd say factorio is imperative. Can's assemblers be compared to pure functions? What's the difference between chaining assemblers and composing functions?

p-himik 2021-06-07T13:15:19.302500Z

The input is destroyed once it's fed into an assembler. You can't direct the same exact input into multiple assemblers, the model just doesn't work.

p-himik 2021-06-07T13:17:30.302900Z

So they aren't even suitable for imperative programming. You can jump through some hoops and build some resemblance of a model, but I don't see how it could be useful in those particular games.

Ben Sless 2021-06-07T13:24:02.303100Z

message passing? :thinking_face:

2021-06-07T13:27:00.304500Z

So some functions would need to just observe the outputs of other functions and not have it physically transported to them?

p-himik 2021-06-07T13:34:25.304700Z

No idea how to interpret the questions. FWIW, and not to be a mood killer, but I sincerely doubt it's possible to make a visual representation of a working program in a useful and flexible way. You can visualize some simple cases, sure. In fact, that's exactly what's going on in some visual programming environments. But you can't do it for a general case - as soon as interop is involved, all will break. Or data mutation. Or higher order functions. Or macros. Or dynamic binding.

Vlad 2021-06-08T11:26:03.306400Z

You can visualise pure functional programming by looking at the types of the terms. For example in Haskell, you could graphically represent the whole program by representing its types. And the pipelines are achieved by connecting one type to the other.

Vlad 2021-06-08T11:30:06.306600Z

But that is only possible due to the fact that the terms are statically typed and the functions are pure. Unsure, if that can be achieved in Clojure due to the way of handling side-effects and dynamic typing.

p-himik 2021-06-08T11:30:53.306800Z

Even without dynamic typing - how would that work when most of the things are maps/vectors/lists/sets?

Vlad 2021-06-08T12:03:12.307Z

All data structures have an associated type. So you would know what type comes in and what type comes out

Vlad 2021-06-08T12:04:53.307200Z

Depends at what level of typing we are talking. You can achieve fully typed terms (where the type depends on the contents) by working with dependent types.

Vlad 2021-06-08T12:05:36.307400Z

a la Idris or Haskell with extensions.

p-himik 2021-06-08T12:07:37.307600Z

Consider this function:

(defn add-field-to-all [k v ms]
  (into (empty ms)
        (map #(assoc % k v))
        ms))
Does what you describe still apply here, assuming the function is used in 15 different contexts where it's unknown in advance what shape each map has?

Vlad 2021-06-08T12:12:24.308Z

So the type of a map in Haskell is Map a b where a is a type variable for the key and b is the type variable for the value. I expect your function’s type signature to be add-field-to-all :: a -> b -> Map a b -> Map a b . As long as the types of the key and value match the type of the map, there is no issue

Vlad 2021-06-08T12:13:14.308200Z

where the last Map is what is returned - it is just currying notation.

p-himik 2021-06-08T12:16:19.308400Z

Now I'm even more confused. So here you are not talking about dependent types and just the real (type x) thing. I'll skip the fact that a -> b -> Map a b is just wrong for the reason you yourself mention. How can knowing (type x) possibly be useful for pure FP visualization?

Vlad 2021-06-08T12:54:31.308600Z

unsure what is wrong about it

Vlad 2021-06-08T12:54:54.308800Z

and the type itself is the visualisation

Vlad 2021-06-08T12:57:05.309Z

and yes, you are correct, these are not dependent types.

Vlad 2021-06-08T12:58:07.309200Z

the type notation is correct and the way it is written shows you what is the next type that you get by using currying and partial application.

Vlad 2021-06-08T12:58:53.309400Z

Map is a type constructor that takes two type variables a b and returns you the type Map a b

Vlad 2021-06-08T12:59:22.309600Z

but a b can be any type

p-himik 2021-06-08T12:59:56.309800Z

> unsure what is wrong about it Maps are heterogeneous. {1 2 :a "c" "x" 1.5} > the type itself is the visualisation I might be misinterpreting but I think the OP wants to have a visualization of a whole program or at least an algorithm, in action. Or, well, in simulated action. Having just a type spelled out is orthogonal IMO.

Vlad 2021-06-08T13:03:25.310Z

A type can have multiple constructors Data MapContent = I Int | S String | … so you can have Map String MapContent which gives you your ability to mix the contents of your type.

Vlad 2021-06-08T13:04:38.310200Z

A program in Haskell is a composition of types - which you know at compilation time as they are statically declared or inferred. If you write out or graphically represent each type, then you have a representation of the program.

Vlad 2021-06-08T13:06:06.310400Z

you can even go to the extent where your map is Map MapContent MapContent and you can have your MapContent to be a recursive type where you can recursively construct your term

p-himik 2021-06-08T13:07:17.310600Z

But when you deal with user data, you can't know the type in advance, unless it's some sort of Any , no?

Vlad 2021-06-08T13:08:24.310800Z

Well you can. Because you define it in advance what it is, or what it can and cannot be.

p-himik 2021-06-08T13:13:21.311Z

> If you write out or graphically represent each type, then you have a representation of the program So suppose I have Map Any Any. And Vector Any. I'll drop Any for brevity. And also assoc :: Map -> Any -> Any -> Map and conj :: Vector -> Any -> Vector. And my program is (assoc {} 1 (conj ["input"] *input*)). How exactly do the types written out above constitute a representation of that program?

Vlad 2021-06-08T13:13:57.311400Z

For example a JSON object which can have a heterogeneous structure could be typed like this:

data Value = Ob Map String Value   
           | Ar [Value] 
           | St String
           | In Int
           | Fl Float
           | Bo Bool
           | Null

p-himik 2021-06-08T13:14:34.311600Z

Yes, and it's a text representation of a type. Not a graphical representation of a program.

Vlad 2021-06-08T13:14:47.311800Z

with that type, you can have a function f : Value -> Value

Vlad 2021-06-08T13:15:02.312Z

which takes a Value and returns a Value

Vlad 2021-06-08T13:15:12.312200Z

that is the representation of your program

Vlad 2021-06-08T13:15:49.312400Z

say you actually want to compose that with another function g : Value -> Int -> Bool

Vlad 2021-06-08T13:16:49.312600Z

g . f : Value -> Int -> Bool

Vlad 2021-06-08T13:18:07.312800Z

And so on

p-himik 2021-06-08T13:21:47.313Z

I'm still not sure what I'm supposed to get from those types. OP asks not about a representation, but specifically about graphical (I assume so given the examples) visualization of data flow. Having f : Value -> Value doesn't show me where f is called. Doesn't let me intuit how often it's called. It's just a statement that f exists with such types. Speaking in terms of Factorio, there's a difference between an assembler with a recipe (which is just like f : Value -> Value) and an assembler with a recipe that's part of a larger pipeline.

Vlad 2021-06-08T13:26:50.313200Z

Well, if your program is made from typed components, then you can literally represent their types - graphically as I have shown you. Because you know from the get go where the program enters - let’s say it is function main . Then all you need to do is represent all the functions that get called when main is executed. Which can either be main :: Value -> Int -> Bool or it can be main :: g . f etc. (or infinitely many functions composed). The one thing you would be unable to represent is what happens inside the type itself (i.e. what happens to the Int once it enters the function) all you know it enters as Int and comes out as something else Value for example.

Vlad 2021-06-08T13:27:32.313400Z

If you are interested, or curious https://bartoszmilewski.com/2021/02/16/functorio/ wrote an awesome article about this.

p-himik 2021-06-08T13:29:59.313700Z

It all, including what's described in the article, still describes write-time and compile-time. Not run-time, which is the crux of the thread. With a proper visualization tool, I would like to see a queue being clogged. With type notations I will only see that a queue might exist in runtime - even its existence is not guaranteed.

p-himik 2021-06-08T13:32:22.313900Z

But I'll let Rob be the judge of whether my assumptions are correct or not.

p-himik 2021-06-08T13:36:28.314100Z

Related to the topic in general - I mentioned visual programming at the start. Here's a demonstration of how something similar is done in Unreal Engine Blueprints: https://youtu.be/vCmxwyw6lkY?t=98

Vlad 2021-06-08T13:36:35.314300Z

Ok, no worries. I am willing to accept my opinion might be wrong or not fit for purpose. Was proposing a different perspective on the matter, and maybe trying to highlight the power of insight that comes with pure functional programming with types. Some things are undecidable, so you cannot describe them with types - like a clogged queue, but the handling of such a situation can be.

👍 1