воскресенье, 19 января 2020 г.

Safety in resource management in Python

Untitled

Yet another example of the type safety in Python maybe a resources management. We can use typing to ensure that resources were released, so no any resoucres leak. Yes, it's possible. The idea is that we will check in compile time with types that resoucres were released by checking that they were returned as appropriated type after its usage. Look the example:

If we will try to ommit return statement in the use_resources() function, then we will get an error Missing return statement from mypy. OK, if we will try to return None, then the error will be Incompatible return value type (got "None", expected "Resources[Free]"). OK, we will return the resource itself: return r. But an error again, now: Incompatible return value type (got "Resources[Hold]", expected "Resources[Free]").

It's a very good error: it says us that we don't release resources, we complete in the Resources[Hold] state. To fix it we will return release_resources(r) and now all is fine. The function use_resources() is wrapper which wraps the usage of them, tracks correct state of their life-cycle. It's typical for Haskell with phantom types, we tried to create one here, in Python. So, the function gets acquired resources, uses them and then releases them, and we will not forget to call release_resources().

PS. Those 3 resource related function can be implemented as class methods of Resources class.

суббота, 18 января 2020 г.

Yet another example of type safety in Python

Untitled

Python can be safe (if we need it) too. Another example is: let's say we want to implement a function unique(). It may looks like:

Truth is that data should be sorted - we don't want to sort it because data items maybe are sorted already. So, instead to write docstring about this our requirement ("data should be sorted") we can specify this requirement as a type, so it will prevent a client from passing of data which was not sorted, so the check will work at compile time:

And if we try to pass something which has not type Sorted then we will get an MYPY error at compile time. It looks like Haskell, ah? :)

суббота, 2 ноября 2019 г.

Transitions correctness checking in compile-time

Untitled

One of the accusations against the OOP is state problems. They are divided into several sub-kinds, one of them is problem of transitions correctness, i.e. control when a transition from one state to another state is incorrect and should be avoided.

People often say that no such problem in FP languages. It's not true, but such illusion exists because of explicit arguments flow from function to function. And this problem appears when all arguments collapse to one argument aggregating all others. In this case the situation is reducing to the case in OOP.

There are multiple canonical solutions - working in compile-time with type-checker help or in run-time:

Run-time solutions are:

  • FSM - the best solution
  • contracts - not so good, but OK
  • asserts - reduced version of contracts

For example, function close() makes sense only if something was opened (let's suppose the result of the open in file):

something like this. The same is possible in an any language.

Compile-time solutions maybe:

  • Linear types
  • Unification (see Prolog) - more general solution
  • Indexed monads (for Haskell, for example)
  • Simple data-types (like data Opened = Opened String; data Read = Read String)

So, if some function must be under such control then even if it does not expect arguments at least one argument must be presented always, it may be term without parameters.

Of course, such a solution exists a long time ago in the OOP. Let's simulate it with a simple functionality - open a file, read it, parse it to integer, close it.

So, we see that we have states: {opened, read, parsed, closed} and each state accepts only limited number of transitions:

open -> read | close
read -> parse | close
parse -> close

That is, each function allows only limited number of next functions in our flow: open allows only a call of read or close and so on. Graphically it looks like:

open ------->.
 |           |
 v           |
read ------->.
 |           |
 v           |
parse ------>.
             |
             v
           close

close is allowed after any state.

We can turn it upside down:

       close
         ^
         |
         +----- parse
         |        ^
         +----- read
         |        ^
         +----- open

and to treat it as classes hierarchy: classes parse, read, open includes common feature - close, they know how to do it, so they inherit the close. Each class has some methods (one or more) and methods expect arguments of these classes types. This will guard us from wrong transitions in compile-time. Actually, this solution is possible even in Python:

All classes implement methods - what they can do. And each of them additionally can close opened file, so they inherit Close class (except Open sure). Their methods are typed with explicit argument state which restricts allowed transition, also it keeps data of the state. Pay attention that call p.close(r) for example, leads to "compile-time" (mypy checking, to be more accurate) error, i.e. transitions are explicit: Parse does not allow a parsing immediately after an opening of the file, without reading of it: p = Parse().parse(o):

error: Argument 1 to "parse" of "Parse" has incompatible type "Open"; expected "Read"
Found 1 error in 1 file (checked 1 source file)

A little improvement maybe to keep Open as instance attribute .opened to allow to close the file from any state, keeping the result of the opening (or something similar).

Classical pattern helps us even better than modern FP languages like Haskell where developers use indexed monads very rarely (and do miss linear types).

But there are not OOP languages where this is solving on type-level too: ATS, Clean, F*...

Reduce points density

Untitled

Sometimes charts have so many points that drawing of them is very highweight. The solution is to reduce points density (to thinning them). Idea is simple: if there are too many samples per linear (of the output device) resolution.

There are different possible approaches: a filtering - it's a bad solution because peaks will be lost or some kind of approximation - thinning, which is better because peaks are saved:

Original chart:

image/svg+xml 23.3MiB 24.1MiB 25.7MiB 42.1MiB 48.3MiB 51.6MiB 58.1MiB 59.1MiB 60.4MiB 62.0MiB 67.0MiB 66.9MiB 66.9MiB 66.9MiB 71.3MiB 86.1MiB 86.3MiB 90.2MiB 90.2MiB 90.5MiB 90.5MiB 90.7MiB 90.7MiB 90.7MiB 90.7MiB 90.5MiB 90.5MiB 90.5MiB 90.5MiB 90.5MiB 90.5MiB 90.5MiB 90.5MiB 90.5MiB 90.5MiB 90.5MiB 90.5MiB 90.5MiB 119.5MiB 119.5MiB 119.5MiB 119.5MiB 119.4MiB 116.2MiB 125.5MiB 125.5MiB 125.5MiB 125.5MiB 125.5MiB 125.5MiB 125.5MiB 125.5MiB 125.5MiB 125.5MiB 125.5MiB 124.9MiB 125.1MiB 125.1MiB 125.1MiB 125.1MiB 124.5MiB 124.5MiB 124.5MiB 124.5MiB 124.5MiB 124.5MiB 124.5MiB 124.5MiB 124.5MiB 124.4MiB 128.3MiB 128.8MiB 129.1MiB 129.1MiB 128.2MiB 128.2MiB 128.2MiB 128.2MiB 128.2MiB 128.2MiB 128.5MiB 128.5MiB 128.5MiB 128.5MiB 128.5MiB 128.5MiB 127.9MiB 127.9MiB 127.9MiB 127.9MiB 127.9MiB 127.9MiB 127.9MiB 127.9MiB 127.9MiB 127.9MiB 127.9MiB 127.9MiB 129.6MiB 129.4MiB 129.4MiB 128.5MiB 128.5MiB 128.5MiB 128.1MiB 133.2MiB 133.2MiB 133.8MiB 133.8MiB 134.3MiB 134.5MiB 134.8MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 134.1MiB 133.9MiB 133.9MiB 133.0MiB 133.0MiB 133.8MiB 134.9MiB 134.9MiB 134.9MiB 134.9MiB 134.9MiB 134.9MiB 134.9MiB 134.9MiB 134.9MiB 134.6MiB 134.6MiB 134.6MiB 134.6MiB 134.6MiB 134.6MiB 135.3MiB 135.3MiB 133.2MiB 133.2MiB 133.2MiB 133.2MiB 133.2MiB 133.2MiB 133.2MiB 133.2MiB 133.2MiB 133.4MiB 133.6MiB 133.2MiB 133.2MiB 133.2MiB 54545480.0 Hz 21826132.0 Hz 2428354.5 Hz 485684.6 Hz 21052674.0 Hz 3165345.3 Hz 2532276.8 Hz 8163273.5 Hz 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 0.010539 sec 0.010596 sec 0.010596 sec 0.010596 sec 0.010596 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010601 sec 0.010602 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010605 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010606 sec 0.010618 sec 0.010618 sec 0.010618 sec 0.010618 sec 0.010618 sec 0.010651 sec 0.010652 sec 0.010652 sec 0.010653 sec 0.010658 sec 0.010658 sec 0.010658 sec 0.010658 sec 0.010658 sec 0.010675 sec 0.010677 sec 0.010699 sec 0.010703 sec 0.010703 sec 0.010703 sec 0.010703 sec 0.010703 sec 0.010703 sec 0.010703 sec 0.010703 sec 0.010703 sec 0.010705 sec 0.010705 sec 0.010705 sec 0.010705 sec 0.010705 sec 0.010706 sec 0.010706 sec 0.010706 sec 0.010706 sec 0.010712 sec 0.010712 sec 0.010718 sec 0.010718 sec 0.010718 sec 0.010718 sec 0.010718 sec 0.010718 sec 0.010718 sec 0.010721 sec 0.010721 sec 0.010724 sec 0.010724 sec 0.010727 sec 0.010727 sec 0.010727 sec 0.010727 sec 0.010727 sec 0.010740 sec 0.010760 sec 0.010797 sec 0.010797 sec 0.010797 sec 0.010797 sec 0.010797 sec 0.010797 sec 0.010797 sec 0.010797 sec 0.010797 sec 0.010797 sec 0.010800 sec 0.010812 sec 0.010839 sec 0.010839 sec 0.010839 sec 0.010840 sec 0.010840 sec 0.010844 sec 0.010848 sec 0.010848 sec 0.010848 sec 0.010853 sec 0.010853 sec 0.010853 sec 0.010855 sec 0.010855 sec 0.010855 sec 0.010855 sec 0.010855 sec 0.010855 sec 0.010855 sec 0.010855 sec 0.010855 sec 0.010855 sec 0.010855 sec 0.010855 sec 153.5MiB 153.5MiB 153.5MiB 153.5MiB 153.6MiB 153.6MiB 153.6MiB 155.9MiB 160.8MiB 164.3MiB 167.7MiB 168.0MiB 168.0MiB 168.0MiB 168.0MiB 168.0MiB 168.0MiB 168.0MiB 168.0MiB 168.0MiB 168.0MiB 168.0MiB 170.4MiB 176.5MiB 176.6MiB 176.6MiB 176.6MiB 176.6MiB 176.6MiB 176.6MiB 176.6MiB 176.6MiB 176.6MiB 176.7MiB 177.7MiB 179.1MiB 179.6MiB 180.0MiB 180.0MiB 180.8MiB 182.0MiB 182.8MiB 182.8MiB 182.8MiB 182.8MiB 182.8MiB 182.8MiB 182.8MiB 182.8MiB 182.8MiB 182.8MiB 182.8MiB 182.8MiB 182.8MiB 182.8MiB 183.0MiB 183.0MiB 183.0MiB 183.0MiB 183.0MiB 183.0MiB 183.0MiB 183.0MiB 183.0MiB 183.0MiB 183.0MiB 183.3MiB 184.9MiB 186.7MiB 188.6MiB 191.6MiB 192.3MiB 192.3MiB 192.3MiB 192.3MiB 192.3MiB 192.3MiB 192.3MiB 192.3MiB 192.3MiB 192.3MiB 192.3MiB 192.3MiB 192.3MiB 192.3MiB 192.3MiB 193.6MiB 194.4MiB 194.4MiB 194.4MiB 194.4MiB 194.4MiB 194.4MiB 194.4MiB 194.5MiB 196.1MiB 197.3MiB 200.0MiB 202.9MiB 202.9MiB 203.2MiB 203.3MiB 203.3MiB 203.3MiB 203.3MiB 203.3MiB 203.3MiB 203.3MiB 203.4MiB 203.4MiB 203.4MiB 203.4MiB 203.4MiB 203.4MiB 203.4MiB 203.4MiB 203.4MiB 203.4MiB 203.4MiB 203.4MiB 203.4MiB 203.4MiB 203.4MiB 203.4MiB 203.4MiB 203.7MiB 206.0MiB 206.6MiB 206.8MiB 207.1MiB 207.1MiB 207.1MiB 207.1MiB 207.1MiB 207.1MiB 207.1MiB 207.1MiB 207.1MiB 207.1MiB 207.1MiB 207.2MiB 207.3MiB 207.3MiB 207.3MiB 207.3MiB 207.3MiB 207.3MiB 207.3MiB 207.3MiB 207.7MiB 208.4MiB 208.4MiB 208.4MiB 209.7MiB 214.2MiB 214.7MiB 215.6MiB 217.5MiB 217.7MiB 217.7MiB 217.7MiB 0.013444 sec 0.013444 sec 0.013444 sec 0.013444 sec 0.013484 sec 0.013484 sec 0.013638 sec 0.013808 sec 0.013923 sec 0.014037 sec 0.014213 sec 0.014221 sec 0.014221 sec 0.014221 sec 0.014221 sec 0.014221 sec 0.014221 sec 0.014221 sec 0.014221 sec 0.014221 sec 0.014221 sec 0.014327 sec 0.014570 sec 0.014810 sec 0.014917 sec 0.014917 sec 0.014917 sec 0.014917 sec 0.014917 sec 0.014917 sec 0.014917 sec 0.014917 sec 0.014917 sec 0.015040 sec 0.015223 sec 0.015452 sec 0.015619 sec 0.015673 sec 0.015673 sec 0.015827 sec 0.016043 sec 0.016089 sec 0.016089 sec 0.016089 sec 0.016089 sec 0.016089 sec 0.016089 sec 0.016089 sec 0.016089 sec 0.016089 sec 0.016089 sec 0.016089 sec 0.016089 sec 0.016089 sec 0.016089 sec 0.016137 sec 0.016137 sec 0.016137 sec 0.016137 sec 0.016137 sec 0.016137 sec 0.016137 sec 0.016137 sec 0.016137 sec 0.016137 sec 0.016137 sec 0.016314 sec 0.016625 sec 0.016783 sec 0.017074 sec 0.017227 sec 0.017309 sec 0.017309 sec 0.017309 sec 0.017309 sec 0.017309 sec 0.017309 sec 0.017309 sec 0.017309 sec 0.017309 sec 0.017309 sec 0.017309 sec 0.017309 sec 0.017309 sec 0.017309 sec 0.017309 sec 0.017361 sec 0.017440 sec 0.017440 sec 0.017440 sec 0.017440 sec 0.017440 sec 0.017440 sec 0.017440 sec 0.017456 sec 0.017619 sec 0.017869 sec 0.018151 sec 0.018361 sec 0.018361 sec 0.018380 sec 0.018389 sec 0.018389 sec 0.018389 sec 0.018389 sec 0.018389 sec 0.018389 sec 0.018428 sec 0.018456 sec 0.018470 sec 0.018470 sec 0.018470 sec 0.018470 sec 0.018470 sec 0.018470 sec 0.018470 sec 0.018470 sec 0.018470 sec 0.018470 sec 0.018470 sec 0.018470 sec 0.018470 sec 0.018470 sec 0.018470 sec 0.018470 sec 0.018571 sec 0.018763 sec 0.018850 sec 0.018936 sec 0.019152 sec 0.019189 sec 0.019189 sec 0.019189 sec 0.019189 sec 0.019189 sec 0.019189 sec 0.019189 sec 0.019189 sec 0.019189 sec 0.019189 sec 0.019246 sec 0.019256 sec 0.019256 sec 0.019256 sec 0.019256 sec 0.019256 sec 0.019294 sec 0.019294 sec 0.019294 sec 0.019457 sec 0.019493 sec 0.019493 sec 0.019493 sec 0.019709 sec 0.019997 sec 0.020059 sec 0.020209 sec 0.020508 sec 0.020548 sec 0.020548 sec 0.020548 sec

Chart with reduced points density:

image/svg+xml 17.0MiB 17.4MiB 23.3MiB 24.1MiB 25.7MiB 42.1MiB 48.3MiB 51.6MiB 58.1MiB 59.1MiB 60.4MiB 62.0MiB 67.0MiB 66.9MiB 71.3MiB 86.1MiB 86.3MiB 90.2MiB 90.5MiB 119.5MiB 119.4MiB 116.2MiB 125.5MiB 124.4MiB 128.3MiB 127.9MiB 129.6MiB 128.5MiB 128.1MiB 133.2MiB 134.3MiB 134.5MiB 133.9MiB 133.0MiB 133.8MiB 134.9MiB 135.3MiB 133.2MiB 133.2MiB 0.0 Hz 63.5 Hz 54545480.0 Hz 21826132.0 Hz 2428354.5 Hz 485684.6 Hz 1910.3 Hz 21052674.0 Hz 3165345.3 Hz 2532276.8 Hz 351708.8 Hz 14.9 Hz 8163273.5 Hz 216515.9 Hz 56.8 Hz 1.9GiB 1.9GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 2.0GiB 0.010477 sec 0.010477 sec 0.010539 sec 0.010596 sec 0.010606 sec 0.010618 sec 0.010618 sec 0.010651 sec 0.010658 sec 0.010675 sec 0.010677 sec 0.010699 sec 0.010706 sec 0.010712 sec 0.010724 sec 0.010727 sec 0.010740 sec 0.010760 sec 0.010797 sec 0.010800 sec 0.010812 sec 0.010839 sec 0.010848 sec 0.010853 sec 0.010855 sec 150.4MiB 150.7MiB 153.5MiB 153.6MiB 155.9MiB 160.8MiB 164.3MiB 167.7MiB 168.0MiB 170.4MiB 176.5MiB 176.7MiB 177.7MiB 179.1MiB 180.0MiB 180.8MiB 182.0MiB 183.0MiB 183.3MiB 184.9MiB 186.7MiB 188.6MiB 191.6MiB 192.3MiB 193.6MiB 194.5MiB 196.1MiB 197.3MiB 200.0MiB 202.9MiB 203.7MiB 206.0MiB 206.8MiB 207.1MiB 208.4MiB 209.7MiB 214.2MiB 214.7MiB 215.6MiB 217.5MiB 217.7MiB 0.013122 sec 0.013185 sec 0.013444 sec 0.013484 sec 0.013638 sec 0.013808 sec 0.013923 sec 0.014037 sec 0.014213 sec 0.014327 sec 0.014570 sec 0.014810 sec 0.014917 sec 0.015040 sec 0.015223 sec 0.015452 sec 0.015619 sec 0.015673 sec 0.015827 sec 0.016043 sec 0.016137 sec 0.016314 sec 0.016625 sec 0.016783 sec 0.017074 sec 0.017227 sec 0.017309 sec 0.017361 sec 0.017456 sec 0.017619 sec 0.017869 sec 0.018151 sec 0.018361 sec 0.018470 sec 0.018571 sec 0.018763 sec 0.018850 sec 0.018936 sec 0.019152 sec 0.019256 sec 0.019294 sec 0.019294 sec 0.019457 sec 0.019493 sec 0.019709 sec 0.019997 sec 0.020059 sec 0.020209 sec 0.020508 sec 0.020548 sec

The algorithm in Python language:

points is the list/iterator of triplet: (x, y, physical_value). x, y - are coordinates of the point, physical_value is the 0y value in some physical units which can be used for a printing value near the chart point or something else. The idea is simple: enumerate over the points and looking for a change of the value (y) greater than some coefficient k (number of pixels of the output device) - while values change is less than k treat them as a line (skip them with continue). But otherwise - treat it as a valuable point and return it (with yield).

This algorithm allows to minimize drawing points number which will improve output performance and will decrease the size of the files with the points without to loose valuable points.