AnnouncementsMatrixEventsFunnyVideosMusicAncapsTechnologyEconomicsPrivacyGIFSCringeAnarchyFilmPicsThemesIdeas4MatrixAskMatrixHelpTop Subs
Try out user generated app platform
1

I wanted to keep my last post on this idea short and to the point. Essentially I wanted to keep it to why it's useful and the code someone would need. If you want to read that version you can find it here:

https://goatmatrix.net/c/Programming/7CcPALRiSA

In this post I want to add all the extra stuff I wanted share. There are really two parts to that.

First is I realize that Nim is not the most widely know language so I also have a version in NodeJS that perhaps will be legible to an additional set of people.

The next part is the far more nerdy way to do it that would have been a distraction to a simple post sharing a utility.

Might as well get the Javascript version out of the way now.

#!/usr/bin/env node                                                                                                                                  
function multiplytext(a,b) {                                                                                                                         
 if(!b) { //We are a core list;
  return a.reduce(multiplytext);
 }
 var out=[];
 for(var atext of a) {
  for(var btext of b) {
   out.push(`${atext} ${btext}`);
  }
 }
 return out;
}

var readline = require('readline');                             // createInterface: read STDIN line-by-line :contentReference[oaicite:0]{index=0}
var rl = readline.createInterface({input: process.stdin});      // no output stream needed for plain reads :contentReference[oaicite:1]{index=1}

var groups = [], current = [];                                   // groups: array of arrays; current: lines in the current group

rl.on('line', line => {
 line = line.trim();                                            // drop whitespace :contentReference[oaicite:2]{index=2}
 if (line === '') {                                             // empty line ⇒ end of a group
  if (current.length) groups.push(current);                     // only push non-empty groups
  current = [];                                                 // reset for next group
 } else {
  current.push(line);                                           // accumulate lines into current group
 }
});

rl.on('close', () => {
 if (current.length) groups.push(current);                      // push the last group if no trailing blank line
 for (var outline of multiplytext(groups))                      // for-of + template strings :contentReference[oaicite:3]{index=3}
  console.log(outline);
});

Ok. Now onto the even nerdier way to do it for the nerdy sake. I wanted to write it in Erlang since that is a language I am learning. I got it done only to find it has a 0.2s delay. This is small and really caused by initializing the BEAM VM Erlang depends on. It's small but doesn't feel perfect especially for small amounts of text. This is unacceptable. Let's turn this into an Erlang based microservice over TCP so we don't have initialization time. Just in case you need it to be fast and highly available. IDK, maybe your text multiplication needs to scale.

First things first install Erlang and a few other things

pacman -Syu
pacman -S erlang rebar3 netcat

Now start a new erlang project

rebar3 new app multserver
cd multserver/src

Now we are going to modify multserver_app.erl

-module(multserver_app).    
-behaviour(application).    
-export([start/2, stop/1]).    
    
start(_Type, _Args) ->    
 Port = case application:get_env(multserver, port) of {ok,P}->P; _->5050 end,    
 Pid = spawn_link(fun() -> multserver:listen(Port) end),           
 {ok, Pid}.    
    
stop(_State) -> ok.  

And the last file to create is our actual server, multserver.erl

-module(multserver).
-export([listen/1]).

listen(Port) ->
 {ok, L} = gen_tcp:listen(Port, [binary,{packet,line},{active,false},{reuseaddr,true},{nodelay,true}]),
 accept(L).

accept(L) ->
 {ok, S} = gen_tcp:accept(L),
 spawn(fun() -> handle(S) end),
 accept(L).

handle(S) ->
 Lines = recv_all(S),
 Groups = lines_to_groups(Lines),
 Outs = multiply(Groups),
 send_all(Outs,S),
 gen_tcp:close(S).

send_all([],S) -> ok;
send_all([H|T],S) ->
 gen_tcp:send(S,H),
 gen_tcp:send(S,"\n"),
 send_all(T,S).

recv_all(S) ->
        case gen_tcp:recv(S, 0) of
             {ok, Bin} -> 
               Len = byte_size(Bin),
               Str = binary_to_list(binary:part(Bin,0,Len-1)), % Remove trailing \n
               case Str of
                 [27] -> []; % Escape signals end
                 _ -> [Str|recv_all(S)]
               end;
             {error,closed} -> []
        end.

% Group lines seperated by an empty line
% Works for both lists and binary
lines_to_groups([[]|T]) -> % End of group for list
 [[]|lines_to_groups(T)];
lines_to_groups([<<>>|T]) -> % End of group for binary
 [[]|lines_to_groups(T)];
lines_to_groups([H]) -> [[H]]; 
lines_to_groups([H|T])->
 [L|G]=lines_to_groups(T),
 [[H|L]|G].

% Perform cartician multiple between all lists using concatenation as the base operation
multiply([]) -> []; % An empty list remains empty
multiply([H]) -> H; % Multiplication with only one operand is itself
multiply([H,T]) -> [X ++ " " ++ Y || X <- H, Y <- T]; % Cartisian multiply exactly two lists
multiply([H1,H2|Rest]) -> % Multiply the tail first because ++ performs better with a short first operand
multiply([H1, multiply([H2|Rest])]).

Add a wrapper script to your ~/bin/mult

#!/bin/bash

{ cat; printf "\033\n"; } | netcat 127.0.0.1 5050

I'm not going to go over configuring or running a release so we'll just use rebar3 shell. cd back to the root of the project root directory and run rebar3.

cd ..
rebar3 shell

The server is running. Open vim and enter a few lines.

Hello
Hi
Ahoy

World
Bye
Hoy

Highlight all the text you want to expand into variants with shift-V. Then run :!mult

You've now expanded your text using the infinitely scalable BEAM VM and the highest paying programming language according to StackOverflow.

Comment preview