Skip to content
/ pirog Public

Golang mapreduce primitives and other cool stuff from perl and javascript.

License

Notifications You must be signed in to change notification settings

Vany/pirog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

38 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

License tag Test goreport

pirog

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.

Useage

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.

MAP(array, callback) array // the cause of this lib

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.

GREP(array, callback) array // from perl

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"

REDUCE(init, array, callback) aggregate // should be in perl

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

EXPLODE(number, callback) array // syntax sugar

Explodes number to range of values

smallEnglishLetters := EXPLODE(26, func(in int) string { return string([]byte{byte('a' + in)}) }) {

KEYS(map) array // from perl

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"}

VALUES(map) array // from perl

Returns full set of values from map to use it further

HAVEKEY(map, key) bool // syntax sugar

Just indicates do we have key in map, or no.

ANYKEY(map) key // syntax sugar

Returns any arbitrary key from map.

ANYWITHDRAW(map) key, value // common operation with mapped job queue

Chooses arbitrary key from map, delete it and return.

FLATLIST(list of lists) list // helper for map

Flaterns list of lists, used when you have MAP in MAP, but need flat list outside.

COALESCE(args...) value // syntax sugar

Return first not empty value from args. Caution, all arguments will be evaluated.

COALESCE("", "", "10", "", "213231243") == "10"

MUST(err)

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))

MUST2 - MUST5

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()
})

REF(value) reference

Sometime functions return struct, and this struct is located on stack, this function moves this struct into heap;

logger := REF(zerolog.New)

SWAPPER(array) func

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)

TYPEOK(interface type conversion) bool // syntax sugar

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) {
    ...	
})

FANOUT(chan) copyer() // closure with channels

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() }  }()
        ...
    }
}

FANIN(chan) generator(), destructor() // closure with channels

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()
	

General purpose functions

Set of functions, you always want to have.

ToJson(any)string // syntax sugar

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.

EXEC(ctx, command, stdin) code, stdout, stderr, err

Same as perl backticks, run external command.

/// look at tests ///

ExecuteOnAllFields(ctx, storage, "method_name") error // reflect cycle for fields

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")

InjectComponents(storage) // reflect cycle for tagged fields

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")

CleanComponents(storage) // reflect cycle

Uninject (set nil) all components (tagged by inject or injectable)

func (c *Component)Stop(ctx context.Context) error
    CleanComponents(&c)
    return nil
}

DEBUG // build dependent constant

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

CHANGEWATCHER(name,var) func(var)

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)
}

Subscription SubscriptionObject := NewSubscription() // map of arrays of channels with mutex

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

REQUEST(req) // struct of request with response channel

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.

About

Golang mapreduce primitives and other cool stuff from perl and javascript.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages