What about cartesian programming?
250bpm.com
250bpm.com
SETL2[2] and SETLX[3] interpreters are available.
Here is a sample program[4] by Keith S. Thompson. I have removed the comment header for brevity.
program Fizz_Buzz;
fizz := {3, 6..100};
buzz := {5, 10..100};
fizzbuzz := fizz * buzz;
fizz -:= fizzbuzz;
buzz -:= fizzbuzz;
num := {1..100} - fizz - buzz - fizzbuzz;
line_map := {[n, "Fizz"] : n in fizz} +
{[n, "Buzz"] : n in buzz} +
{[n, "FizzBuzz"] : n in fizzbuzz} +
{[n, n] : n in num};
loop for i in [1..100] do
print(line_map(i));
end loop;
end Fizz_Buzz;
[0] http://www.settheory.com[1] http://wiki.c2.com/?SetlLanguage
[3] https://randoom.org/Software/SetlX
[4] https://raw.githubusercontent.com/Keith-S-Thompson/fizzbuzz-...
Edited to fix formatting of footnotes and add newlines.
def setrange(first, next, last):
return set(range(first, last+1, next-first))
fizz = setrange(3, 6, 100)
buzz = setrange(5, 10, 100)
fizzbuzz = fizz & buzz
fizz -= fizzbuzz
buzz -= fizzbuzz
num = setrange(1, 2, 100) - fizz - buzz - fizzbuzz
line_map = dict({n: "Fizz" for n in fizz}.items() +
{n: "Buzz" for n in buzz}.items() +
{n: "FizzBuzz" for n in fizzbuzz}.items() +
{n: n for n in num}.items())
for i in range(1, 100+1):
print(line_map[i])
Some SETL features Python lacks:• No ellipsis notation for ranges; must use the "range" function and add one to the maximum value, or define a custom "setrange" function
• Can't concatenate dictionaries; must concatenate their "items" lists and convert back to a dictionary
• No consistent map-lookup notation; collections are indexed with brackets, but functions are called with parentheses
a = {* * b, * * c}
(Python 3.5!)
Unless this is doing something unintended?
> No consistent map-lookup notation;
You can use .get() in Python on a dictionary :)
Here is a fizzbuzz in Spitbol. Probably not the best way to do it.
var a = 0;
start
+a = a + 1;
break
+lt(a,101) :f(end);
x01
+x = (remdr(a,3) + remdr(a,5)); eq(x,0) :s(x06);
x02
+y = remdr(a,3); eq(y,0) :s(x04);
x03
+z = remdr(a,5); eq(z,0) :s(x05)f(x07);
x04
+output = 'fizz' :(start);
x05
+output = 'buzz' :(start);
x06
+output = 'fizzbuzz' :(start);
x07
+output = a :(start);
end
More: http://www.hakank.org/setl/fizzbuzz.setlHe also has pages on APL and k.
> B. K. Oxley (binkley) (guest) 30 Apr 2017, 11:42
> Does SQL match your notion of Cartesian paradigm? It's declarative (not procedural or functional), and works over sets. The "larger scope" would be the whole set of data; the "smaller scopes" would be projections, selections, etc. "
My immediate thought also was set-based programming, and in turn SQL and its kin.
https://en.wikipedia.org/wiki/Set_theoretic_programming
It was also used in formal specifications like Z. Also, used in one, verifying compiler that cheated the spec-to-code correspondence by straight-up turning the logical specs into executable Prolog. On database side, it would be Datalog for you to look into to explore logic programming + queries. Plenty of info out there on that with one DB (Datomic) built around it.
https://en.m.wikipedia.org/wiki/Relational_algebra?wprov=sfl...
"Relational programming using SQL with a focus on generation of data rather than querying it" is basically my job since 4-5 years. And i can't believe I'm the only one SQL guy out there doing that.
It very effective for data manipulation once you get the correct mindset.
I've tried to rewrite the testsuite example to SQL, but it's quite ugly. Also, I am not sure how to model the inheritance relationship:
CREATE TEMP VIEW boxes AS
SELECT 'box1' AS hostname, 'linux' AS os, 'x86-64' AS arch, 8 AS ram UNION
SELECT 'box2', 'freebsd', 'arm', 16 UNION
SELECT 'box3', 'windows', 'x86-64', 4 UNION
SELECT 'box4', 'illumos', 'sparc', 4;
CREATE TEMP VIEW compilers AS
SELECT 'gcc' AS binary, '4.8.4' AS version, '-o' AS output_option UNION
SELECT 'clang', '3.4.1', '-o' UNION
SELECT 'msvc', '15.00.30729.01', '/Fe';
CREATE TEMP VIEW tests AS
SELECT 'frobnicate' AS name, 'frbonicate.c' AS sources UNION
SELECT 'loadtest', 'loadtest.c helpers.c' UNION
SELECT 'end2end', 'end2end.c helpers.c';
CREATE TEMP view testsuite AS
SELECT *, compilers.binary || ' ' || tests.sources || ' ' ||
compilers.output_option || ' ' || tests.name AS cmdline
FROM boxes, compilers, tests
WHERE NOT(tests.name = 'loadtest' AND boxes.ram < 8);
SELECT * FROM testsuite; WITH
boxes(hostname, os, arch, ram) AS (VALUES
('box1', 'linux', 'x86-64', 8),
('box2', 'freebsd', 'arm', 16),
('box3', 'windows', 'x86-64', 4),
('box4', 'illumos', 'sparc', 4)
),
compilers("binary",version,output_option) AS (VALUES
('gcc', '4.8.4', '-o'),
('clang', '3.4.1', '-o'),
('msvc', '15.00.30729.01', '/Fe')
),
tests(name,sources) AS (VALUES
('frobnicate' , 'frbonicate.c' ),
('loadtest', 'loadtest.c helpers.c'),
('end2end', 'end2end.c helpers.c')
)
SELECT *, compilers.binary || ' ' || tests.sources || ' ' ||
compilers.output_option || ' ' || tests.name cmdline
FROM boxes, compilers, tests
WHERE NOT(tests.name = 'loadtest' AND boxes.ram < 8)
Here is a fiddle to play around... http://sqlfiddle.com/#!15/9eecb7db59d16c80417c72d1e1f4fbf1/1...But this is a stand alone query, SQL really become useful IMHO if data is stored inside the DB or gathered automatically by the db. For the inheritance I'm really clueless, it depend on what you try to achieve and i'm just a DB guy.
As for the comment about stored data, that's actually the reason why I haven't considered SQL an option at first. The use case doesn't involve any stored data, so SQL intuitively doesn't sound like the right tool for the job.
In the end, I think the cartesian paradigm can be expressed using SQL, but you have to twist your traditional SQL mindset to do that. You have to forget about tables etc.
-- omitting the definitions as trivial...
cmdline (Suite box (Compiler cmd _ out_opt) (Test binary sources))
= intercalate " " [cmd, sources, out_opt, binary]
testSuites = [Suite srvr comp test |
srvr <- [box1, box2, box3, box4],
comp <- [gcc, clang, msvc],
test <- [frobnicate, loadtest, end2end],
not (binary test == "loadtest" && ram srvr < 8)]
(Note that Haskell lets you specify what `==` means so probably the above should say `test == loadtest` more directly...)If there are other verbs then it might not be 100% equivalent. For example one might want a sort of "Non-uniform random choice monad," let's say a monad of "Choosers", which will quickly generate a random candidate test suite. If you wanted to support both, you'd want to quickly define:
class Monad m => MChoice m where
choose :: [x] -> m x
invalid :: m x
invalid = choose []
mfilter :: Bool -> m ()
mfilter True = return ()
mfilter False = invalid
instance MChoice [] where choose = id
Then you can say, testSuites :: MChoice m => m TestSuite
testSuites = do
srvr <- choose [box1, box2, box3, box4]
comp <- choose [gcc, clang, msvc]
test <- choose [frobnicate, loadtest, end2end]
mfilter . not $ test == loadtest && ram srvr < 8
return $ Suite srvr comp test
In this respect maybe Cartesian programming represents a generalization of the list monad. q)datacenter: ([] name: `newyork`london`tokyo; webservers: 7 4 2);
q)plan::update backends: webservers * 10 from datacenter
q)webservers::select name, cmdline:`:/usr/bin/webserver, instances:webservers from plan
q)backends::select name, cmdline:`:/usr/bin/backend, instances:backends from plan
q)1@.j.j backends;
[{"name":"newyork","cmdline":":/usr/bin/backend","instances":70},
{"name":"london","cmdline":":/usr/bin/backend","instances":40},
{"name":"tokyo","cmdline":":/usr/bin/backend","instances":20}]
kdb views track dependencies, so we can get Seattle represented with minimum effort: q)`datacenter insert (`seattle; 6);
q)1@.j.j backends;
[{"name":"newyork","cmdline":":/usr/bin/backend","instances":70},
{"name":"london","cmdline":":/usr/bin/backend","instances":40},
{"name":"tokyo","cmdline":":/usr/bin/backend","instances":20},
{"name":"seattle","cmdline":":/usr/bin/backend","instances":60}]
What was "B" supposed to be twice of "A"? q)A:([] x:10?10);
q)B::update x:x*2 from A
q)A`x
8 1 9 5 4 6 6 1 8 5
q)B`x
16 2 18 10 8 12 12 2 16 10
q)update x:10?10 from `A;
q)A`x
4 9 2 7 0 1 9 2 1 8
q)B`x
8 18 4 14 0 2 18 4 2 16
And the syntax is much nicer than JavaScript.e.g.
const X = ['a', 'b'];
const Y = [1, 2];
const res = cartesianMap(X, Y)((x, y) => [x, y]);
console.log(res); // [['a', 1], ['a', 2], ['b', 1], ['b', 2]]https://github.com/sustrik/cartesian
Here's what author told me on Lobste.rs about this:
"As for the motivation, it’s twofold.
Firstly, I have to deal with complex configurations on daily basis and most of what’s out there is pretty ugly. Approach in Cartesian tries to solve the ugliness without resorting to exotic paradigms. It’s (I think) something that your average JavaScript developer would be comfortable with, yet powerful enough to describe really complex systems.
Secondly, it’s a methodological experiment. I think it’s fair to say that inheritance has mostly failed us when we tried to model the complexity and irregularity of the real world. Therefore, this is a sketch of an alternative paradigm, one that uses cartesian products instead of inheritance. (But, as mentioned in the README, it can even be combined with classic inheritance.)"
I'll message author about the thread to see if he wants to answer any questions.
I've long thought that configuration can be modeled as intersecting N-dimensional subspaces, where N is the number of configuration axes (e.g. production vs test vs dev is one axis, as are datacenter location, machine role, etc). You can say that all test machines enable feature flag X, or everything in datacenter Y should use network setting Y.net - and these are cross-cutting concerns that don't lend themselves to hierarchical composition (and thus inheritance is out).
The configuration settings themselves have a scope in the configuration space. But then you need to evaluate it mentally to sanity-check it: are all the spaces covered correctly? Are there any odd overlaps?
There's a pairing with table-based exhaustive testing. Perhaps the cartesian product of all the axes results in too many distinct N-cubes to test individually, but predicates might be written that act as sanity checks, and testing for exhaustive coverage of predicates over the configuration space is possible (e.g. "every box has a disk space allocation", "boxes running service D have at least X disk space", etc.).
Anyhow, it's rattled away in my head for decades and has leaked out into implemented systems a couple of times, but never in a fully formalized or packaged way.
I worked on a Java library which also tackles the test variants problem: https://github.com/square/burst
With this library you would write something like
enum Box { BOX1, BOX2, BOX3, BOX4 }
enum Compiler { GCC, CLANG, MSVC }
@Test void loadtest(Box box, Compiler compiler) { ... }
I'm not sure if there are any big uses for cartesian products outside of test variants though. Probably not enough to justify making it a first-class language feature.most programmers would just not use Prolog (too exotic) while they may be OK with JavaScript
That just makes me sad. I don't know how else to put it.
Learning new languages used to be fun, I think. Now we gotta mollycoddle programmers into thinking about "new" paradigms, which are really not new at all, by wrapping it all up in javascript otherwise they run away screaming?
I hope that's not true.
- Shows limited knowledge of the author (that imperative and functional are the only paradigms; ever looked at wikipedia page on programming paradigms [1], especially the sidebar on the right?)
- There is no such thing that goes by the name Cartesian Programming (likely the author giving his own name to something known with a different name).
- In the text, author starts discussing the paradigm with a tone that assumes the reader knows what it is.
All in all, a low effort post.
The author doesn't claim those are the one two paradigms.
The author effectively calls out that there is no existing thing that goes by that name - how could he, when in the first paragraph he says he has never heard of it elsewhere.
He succinctly states what it is before going into more detail.
It's as if we read completely different articles.
All in all, a low effort comment.
"Sure, we have imperative and functional. But what about Cartesian programming?"
> The author doesn't claim those are the one two paradigms.
I never said the author made that claim (I said it shows the state of his knowledge). But for any statement along the lines "Sure we have imperative and functional. But what about X?"
Well what about X? There are plently of X's around. If you look at the right sidebar on the page I linked, the number of X's are probably 30 to 40. Has the author studied and understood those 30 to 40 before asking "what about X"?
What does the reader learn about X if the author doesn't put in the effort to study at least half of the 30 to 40 paradigms and then put his own thoughts in proper context? Well I guess the author might realize what he/she is thinking of is so well-known under another name that his way of presenting the content is simply causing more unnecessary terminology and bloat on the internet.
Just my two cents.
Visit the website again and check out who the author is, and you'll be embarrassed for the rest of the day :)
Here's what it says on the first line of his home page: "Website of Martin Sústrik, creator of ZeroMQ, nanomsg, libdill, libmill and ribosome."
Even aside from the "limited knowledge" issue, he doesn't say anything you're accusing him of saying. Chill out and be nice man.