Golang mapreduce primitives and other cool stuff from perl and javascript.
Main idea is to use commonly known and well proven constructions introduced in other languages. Constructions have distinguishable appearance and pretend to be a part of the language rather than just functions.
Just import it as is, but if you want and have enough dare, use special symbol to package reference like
import . "github.com/vany/pirog"
Then just use it.
This is part of mapreduce and almost full copy of perl's map. It transforms input array to output array with callback function.
type Person struct {
FirstName string
SecondName string
}
people := []Person{{"Vasya", "Pupkin"}, {"Vasya", "Lozhkin"}, {"Salvador", "DalĂ"}}
out := MAP(people, func(p Person) string{
return fmt.Sprintf("%s %s", p.FirstName, p.SecondName)
})
out
now is []string containing concatenated names.
This is filter, that leaves only that elements that trigerrs callback function to return true
fakePeople := GREP(out, func(s string) bool {
return strings.HasSuffix(s, "Pupkin")
})
fakePeople
now is []string and contains just "Vasya Pupkin"
Takes array and applies callback function to aggregate object and each element of array. Starts from init.
x := REDUCE(1+0i, EXPLODE(6, func(i int) complex128 {
return 0 + 1i
}), func(i int, in complex128, acc *complex128) {
*acc *= in
})
// rounds dot for 3Ď€ so result will be -1
Explodes number to range of values
smallEnglishLetters := EXPLODE(26, func(in int) string { return string([]byte{byte('a' + in)}) }) {
Returns just full set of keys from map to use it further
artistsMap := map[string]string{
"Vasya":"Lozhkin",
"Salvador":"DalĂ",
}
AllLozhkins := GREP(KEYS(artistsMap), func(in string) string }{
return artistsMap[in] == "Lozhkin"
})
AllLozhkins
will be []string{"Vasya"}
Returns full set of values from map to use it further
Just indicates do we have key in map, or no.
Returns any arbitrary key from map.
Chooses arbitrary key from map, delete it and return.
Flaterns list of lists, used when you have MAP in MAP, but need flat list outside.
Return first not empty value from args. Caution, all arguments will be evaluated.
COALESCE("", "", "10", "", "213231243") == "10"
Validates err for nil and panics other way. When you in CLI or sure that here can not be an error.
MUST(json.NewEncoder(buff).Encode(object))
Same as must, but returns values number at the end of name is number of the arguments.
files := GREP(MUST2(os.ReadDir(".")), func (in os.DirEntry) bool {
return !in.IsDir()
})
Sometime functions return struct, and this struct is located on stack, this function moves this struct into heap;
logger := REF(zerolog.New)
Same as reflect.Swapper(), generates function of two int params to swap values in specified array
arr := []os.File{f1,f2,f3}
swapFiles := SWAPPER(arr)
swapFiles(1,2)
Returns just ok part from conversion, used for checking interface type
v := any(os.File{})
if TYPEOK(v.(os.File)) { ... }
SEND(ctx, chan, val) // hidden select
Send to unbuffered chan, exit if context canceled
go func() {SEND(ctx, chan, "value"); print("continue execution")}()
cancel()
NBSEND(chan, val) bool // hidden select
Send to unbuffered chan, nonblocking
if NBSEND(chan, "value") { ... }
RECV(ctx, chan) val, bool // hidden select
Receive blockingly from channel, exit if context cancelled
if val, ok := RECV(ctx, ch); ok {
...
}
NBRECV(chan) val, bool // hidden select
Receive non blockingly from channel
if val, ok := NBRECV(ch); ok {
...
}
WAIT(ctx, chan, cb()) // hidden select
Nonparallel promise on channel.
go WAIT(ctx, ch, func(T) {
...
})
Creates copyer of chan, all events put in base chan will be copied to a copies. All chan events will be handled properly. If original chan closing all copies will be closed.
func serveClient(original chan T) {
generator := FANOUT(original)
for ... {
c, d := generator()
defer d()
go finc(){ for msg := range c { ... ; ... ; d() } }()
...
}
}
Creates attacher to sink chan. All messages from attached chans will be copied to main chan.
generator, destructor := FANIN(ch)
go func(){ for range ch { ... } }()
c1, c2 ,c3 := generator(), generator(), generator()
c1 <- msg1
c2 <- msg2
...
destructor()
Set of functions, you always want to have.
Returns json representation of argument or dies.
jsonPeople := MAP(people, func(p Person) string{ return ToJson(p) })
jsonPeople
becomes slice of strings contained json representation of people
array elements.
Same as perl backticks, run external command.
/// look at tests ///
Executes method_name(ctx)
on all non nil interface fields in storage, used to initialize application.
app := App{
Connection: fancy.NewConnection(cfg),
}
ExecuteOnAllFields(ctx, &app, "InitStage1")
Takes all *struct and interface fields and puts it in its fields found fields by type.
type Component struct { L *Logger `inject:"logger"`}
app := App{
Logger: new(Logger), `injectable:"logger"`
Component: new(Component),
}
InjectComponents(&app)
app.Component.L.Info("Component now have logger injected")
Uninject (set nil) all components (tagged by inject or injectable)
func (c *Component)Stop(ctx context.Context) error
CleanComponents(&c)
return nil
}
Constant based on debug build tag. Code in if statement will not be compiled if DEBUG is false (debug tag not set)
if pirog.DEBUG { logger.Debug(<some toughly evaluted info you want not to evauate >) }
go run -tags debug programm.go
Returns if variable was changed from previous call. If DEBUG is set, prints point in code to stdout.
cw := CHANGEWATCHER("mainchangewatcher", "")
for {
// some code that can change variable
_ = DEBUG && cw(variable)
}
Subscribe to some type of event or object ids.
s := NewSubscription[string, any]()
achan, bchan := s.Subscribe("now"), s.Subscribe("now")
s.Notify("now", time.Now())
... <- achan ...
s.Close("Now")
... _, closed := <- bchan
Create answerable request to send it over channel
c <- REQUEST[ReqType, RespType](req).THEN(ctx, func(ctx context.Context, resp RespType) {
// Do something with resp
})
go func() {
r := <- c
// calc resp
r.RESPOND(ctx, resp)
} ()
Requests and pull requests are welcome.