Thursday, 2 July 2020

(practical-python->racket exercise 1.7 mortgage)

The first exercise is to calculate the total payments taken to pay off a 30-year fixed rate mortgage. David Beazley provides an initial solution to get his students started.


I started to think through how best to convert David's example into Racket. Almost without thinking, I fell back on the techniques covered in the first parts of How To Design Programs (HTDP). I designed a structure to hold the data and then wrote a recursive function to process it. (How else to replicate a while loop in Racket?) I added a couple of simple tests to check it was working. The program gives the same result as its Python equivalent but with its structure and function definitions, it lacks the immediacy of it:

#lang racket
(require test-engine/racket-tests)

; data definitions
(define-struct mortgage 
  [balance rate payment num-payments total-paid])

; data
(define daves-mortgage
  (make-mortgage 500000.00 0.05 2684.11 0.00 0.00))

; functions
(check-within (apply-interest 100 1.20) 110.0 1e-5)
(define (apply-interest balance rate)
  (* balance (add1 (/ rate 12))))

(check-within (pay daves-mortgage) 966279.6 1e-5)
(define (pay mortgage)
  (cond
    [(<= (mortgage-balance mortgage) 0) 
     (mortgage-total-paid mortgage)]
    [else (pay (make-mortgage
                (- (apply-interest
                    (mortgage-balance mortgage)
                    (mortgage-rate mortgage))      
                (mortgage-payment mortgage))
                (mortgage-rate mortgage)
                (mortgage-payment mortgage)
                (add1
                 (mortgage-num-payments mortgage))
                (+ (mortgage-total-paid mortgage)
                (mortgage-payment mortgage))))]))

(test)

; The "program"
(display "Total paid ")
(displayln (pay daves-mortgage))

So I set about trying to better emulate the Python version. In my first attempt, I passed indvidual values to the recursive function instead of a structure. It looked a little more like the Python version and was roughly half the length of the "HTDP" attempt (in terms of lines of code). But it still didn't feel very "practical".

The only way I could think of to make the code look more like the Python was to use "global, mutable variables" (in the same way that the Python does). In the part of HTDP that I had studied, there was never any mention of changing data, everything was immutable. I had somewhere seen "set" used in Racket code so I looked it up in the Racket Reference and did my worst. The code does look much more more like the Python but must rank as some of the worst Racket code ever written:

#lang racket

(define principal 500000.0)
(define rate 0.05)
(define payment 2684.11)
(define total_paid 0.0)

(define (pay)
  (cond
    [(<= principal 0) total_paid]
    [else (set! principal 
                (- (* principal (+ 1 (/ rate 12)))
                   payment))
          (set! total_paid (+ total_paid payment))
          (pay)]))

(display "Total paid ")
(displayln (pay))

Wednesday, 1 July 2020

(practical-python->racket 1.3 numbers)

The first challenge that I faced in translating the examples of Practical Python Programming - 1.3 Numbers was the differences in the number type systems between Python and Racket.

Python has a flat number type system with four types: booleans, integers, floating point and complex (imaginary numbers).

Racket has a heirarchical number type system. There are two main branches in the heirarchy, exact and inexact. This is a somewhat simplified view of the Racket number type hierarchy:
    exact
      integer      e.g. 10 - like a Python integer
      rational     e.g. 1/2
      complex    e.g. 1 + 2i
    inexact
      real          e.g. 2.0 - like a Python floating point
      complex    e.g. 2.1 + 3.3i

Racket also has two specialists number types, flonum (floating point) and fixnum (integer), that are used when there is a need for improved performance. These have their own maths operators. (I haven't looked into them any further at this stage.)

When "translating" the Python code, I have equated its integer with Racket's exact and its floating point with Racket's inexact.

The second came from Python's boolean type. It's a very raw C-language type with very few barriers to how a programmer can use it. For all I know, Racket's boolean type may similarly be implemented using integers. However, as far as the Racket programmer is concerned, it supports only two values: true and false.

I didn't want my script to just raise an error and stop when I tried to add a number to a boolean. After searching the docs and a little trial and error, I worked out how to trap an error:

   (racket '(define c 4))
   (racket "(+ c true)")
   (expect "Contract violation")
   (with-handlers ([exn:fail:contract?
                 (lambda (err) 
                  (displayln "Contract Violation"))])
                 (+ c true))

The third was tame in comparison, working out how to convert an inexact float to an exact integer. I came up with using: 

   (inexact->exact (floor my-float-value))

Take a look at the numbers.rkt script, then run it either in Dr Racket or the Racket repl. Do the result make sense?

Tuesday, 30 June 2020

(practical-python->racket approach)

Superficially, it seemed logical to start at the beginning. But upon reading the first few pages, it soon became clear to me that attempting to "translate" them requires a deeper knowledge of Racket than I have today. I thought it best to start with some easier material. So, I started with the introductory material about numbers, strings and lists that was easier to translate. I'll come back to the start later.

Each section of the course includes examples and exercises. My approach is to write a single Racket script which includes all the examples. (Is it even correct to refer to a short Racket program as a script?). I write a separate Racket program for each of the exercises. I'll write any notes I might have in this blog. I'll store the Racket programs in GitHub.

After a few false starts I managed to work out how to get the Racket that I've written to run in both Dr Racket and the Racket repl. I had written a few short Racket functions to reduce the amount of typing to complete the example scripts.

For example, instead of writing (displayln "Python: a = True") I can write (python "a = True"). It's basic text substitution. 

The function that I use to "expand" (racket value) is a little more sophisticated / complicated depending on you point of view.

(define-namespace-anchor anchor)
(define ns (namespace-anchor->namespace anchor))
(define (racket rkt)
  (cond
    [(list? rkt) (display "Racket: ")
                 (writeln rkt)
                 (eval rkt ns)]
    [(string? rkt) (displayln (string-append "Racket: " rkt))]
    [else          (displayln "Racket: Oops")])) 

The racket function will display its arguments and also evaluate the argument if it is a list. 

What took the time to figure this out was that you don't need to provide the namespace to the eval function when you run code in the repl unless you  specify #lang racket. It seems that Dr Racket insist on you specifying a language. So the code wouldn't run in Dr Racket.

I couldn't see a quick solution staring at me from the pages of the documentation. As I was really just performing text substitution, I thought I'd be better off writing a macro.

It took me number of attempts but I did eventually manage to get a macro to do pretty much the same thing. The issue that I came across was that I couldn't work out how to just expand the code with the macro and not evaluate it. So when I ran the script, all the macros were run before the rest of the code and the output was not in the order I wanted. (I guess I could have translated all my "expander" function to macros but I'll leave that for another time).

I went back to the docs and found that what I need to get my racket function working was to create and provide it a namespace. I first used make-base-namespace to create the namespace. I later found that I needed more modules than provided in racket/base to emulate the Python examples. Section 15.1 eval in The Racket Guide had a simple example of creating a namespace based on the "language" being used.

On to the first translation 1.3 Numbers.



Friday, 26 June 2020

(practical-python->racket introduction)

Recently, renowned Python expert David Beazley generously open-sourced his Practical Python Programming course. My Python skills are steadily improving through consistent use and watching talks and tutorials on YouTube. I've learnt a lot from David's talks and tutorials.

Sometimes I feel that my understanding of languages can be more theoretical than practical. I spent too much time learning and not enough time applying my knowledge. Practical Python Programming sounded exactly what I needed. I set out dutifully entering the examples into Python and doing the exercises. It was helpful. I even learnt that the Chicago Transit Authority #22 bus doesn't run at night.

What is Practical Programming though? Well to me, it's writing programs, short or long, to get things done quickly and efficiently. Effectively, it's turning a computer into a personal productivity tool. My current practical programming project helps me analyse companies. It is putting knowledge to use. 

I was mid-way through Chapter 1 of Practical Python Programming when a sudden thought broke my concentration. "This is just the type of material that would help me move forward with Racket. Hmmm, perhaps I should try coding the examples and exercises in Racket?"

So I tried out the introduction to Lists. I found I was able to "translate" the examples to Racket with some difficulty and quite a bit of "Doc Diving". During the process I expanded my knowledge of Racket and reinforced what I already knew. It seemed like a good idea to continue and get as far through the course as I could. I'll try to write about my experience in doing so in this blog as it helps me to remember what I've learnt.

I contacted David Beazley and he was happy for me to blog about my efforts to "translate" his Python to Racket. Though this is not in anyway his endorsement of the contents.

Another word of warning, this is very much a pseudo-mechanical translation. It's turning idiomatic Python into Racket. The code will certainly not be idiomatic Racket code. It could well turn out to be idiotic Racket code. Let's see.

You can read about my approach or go straight to the first section.




Monday, 22 June 2020

Learning Racket

I decided to try to learn Racket. Why?

It's not directly relevant to my current programming in JavaScript, Lua, Python and Rebol. Most of my programming work has been, is, and probably will be, run of the mill application software development. Since I started working as a programmer, languages such as Lisp, Forth and Prolog have always held an element of intrigue for me. They are different from the languages that I use.

Rebol is said to have both Lisp and Forth in its genes. But somehow, it doesn't hold the same level of intrigue. Perhaps its because the majority of people in the Rebol community were intent on using Rebol rather than discussing its design and implementation. The focus was far more on what you could do with Rebol than what you could do to Rebol. It could also be that, to quote a friend, "I wrote Rebol code the same way I wrote COBOL".  On the other hand, it could be that I have reached a reasonable level of fluency and the intrigue has faded during the process.

I didn't start out to learn Racket because I found it intriguing though. I had something else in mind. Functional programming is a hot topic these days (at least in the circles I'm connected with). It is the "only" answer to make use of the ever increasing number of cores in CPUs according to many of its proponents. Object oriented programming is frowned upon in the same circles these days. I use functions in my programs. I use objects in my programs. 

However, I feel that I have a superficial understanding of functional programming. I've taken a quick look at Erlang, studied books on functional programming in JavaScript and watched YouTube videos promoting functional programming in Python. I have an equally superficial understanding of object-oriented programming. I've half-heartedly studied Java in the past. I have learned a little Ruby and dipped in to the world of "proper" object-oriented programming. 

I believe that I would be a better programmer if I was more fluent in both functional programming and object-oriented programming metaphors. I decided to try to learn a functional style programming language and, after that, to properly try object-oriented programming. 

I've even identified two simple projects that I could use as learning vehicles. I know from experience, that I will only reach the level of fluency in a style of programming when I can use it productively by writing useful code in it.

It was easy for me to choose to use Ruby as a medium for object-oriented programming. The choice was not so easy when it came to functional programming.

I was encouraged to learn Elixir. It seems to have taken over some of people's previous enthusiasm for Ruby. People mentioned "SICP" in glowing terms insinuating that it is the seminal functional programming text. You can follow SICP using Racket. In the past, I had heard favourably of Racket. I took a look. It is very professional presented with a lot of documentation. It is easy to install and get started. 

I decided to work through "How To Design Programs" or HTDP as it appears to be snappily known. I chose the word work carefully. I completed around 40 percent of HTDP and did about 90 percent of the 240 exercises I have so far encountred  I probably spent an hour a day studying HTDP. Sometimes it felt a little like "Wax on, wax off" as the exercises felt a little repetitious to me.

It was taking too long to hold my attention, so I switched to The Racket Guide. I made faster progress but also soon realised that I'll need to complete reading most of the guide before I can start writing Racket programs.

At the moment, I still feel that I have a way to go before I can start on even a small project in Racket to start building some actual fluency. I'm not sure that I have the stamina to finish The Racket Guide as an academic exercise.

Thankfully, I have stumbled across another approach which has boosted my enthusiasm which you can read about at (practical-python->racket introduction).



Saturday, 28 April 2018

Swift divideWithOverflow doesn't - Update 2

Quite a long time ago I came across an unusual circumstance that crashed Swift int32divideWithOverflowI reported the bug to see the details.

Since then ...divideWithOverflow has morphed into divideReportingOverflow and it fixes the issue in some cases but not quite all yet.

Sunday, 25 February 2018

Installing Watir and Python Selenium on macOS

I have been using Watir on and off for quite a long time, seeing it develop from Watir through Watir-Webdriver and back to Watir. I needed to install its latest incarnation and hit a few problems. The problems were caused more by my impatience in not reading through the notes on the Watir website than anything else.

At the same time, I decided to install the Python Selenium package to try it out. It also didn't work out of the box. Once again, the problem was that I hadn't been sufficiently thorough in reading the instructions.

The main problem with both was the need to install the Firefox and Chrome web drivers and to configure Safari's. After a little trial and error and reading through  the documents, the simplest way came to the surface.

I installed the Firefox and Chrome web drivers using Homebrew: 
    brew install geckodriver
    brew install chromedriver

My Safari installation was already configured to show the Develop menu and I have Allow Remote Automation menu item selected. From the Watir Safari Driver page, I found that I need to run safaridriver once from the command line to give it the proper authorities. I needed to run it from an administrator account:
     sudo /usr/bin/safaridriver

After that both Watir and Python Selenium both will happily load Safari, Firefox and Chrome when run from a macOS user account.