programming languages – Hamel’s Blog - Hamel Husain
Хамел Хусейн делится главными выводами после прохождения трёхчастного курса Coursera «Programming Languages» Дэна Гроссмана; код его (приватного) репозитория лежит на GitHub. В части A изучается Standard ML (SML) — статически типизированный язык с мощным выводом типов, рекурсией вместо циклов, сопоставлением с образцом через case, замыканиями, хвостовой рекурсией, каррированием и композицией функций. Часть B посвящена Racket (родственнику Lisp и Scheme) с динамической типизацией, thunk'ами, ленивыми вычислениями, макросами и написанием собственного маленького интерпретатора. В части C рассматривается Ruby — чисто объектно-ориентированный язык с динамической типизацией, блоками, yield, лямбдами и наследованием. Отдельно автор описывает настройку Vim как IDE для SML с плагином jez/vim-better-sml и конфигурацией .vimrc.
High level takeaways after completing the 3-Part Coursera class Programming Languages with Dan Grossman.
Your GitHub repo for this class (private) is here.
SML (Standard ML) Part A
hd, tl and cons.let is very important (which also allows you to bind local/private functions as well)cons allows you to append to the beginning of a listNONE or SOME v- You can have nested patterns
- You can pattern match against function arguments which allow for really nice syntax for achieving multiple dispatch type of functionality.. (not sure about python)
- You can pattern match against types as well as data structures.
- You can have constants in there as well.
case name NameType name => ... | (first, "MyLastName") => ... | (first, last) => ... | name => ... | _ => ...fn keyword is used to define anonymous functions.lexical scope which means function is evaluated in the environment where the function was defined. dynamic scope, which is usually not desired, is the alternative where the function is evaluated in the in the environment it is called.closure. The call stack has a snapshot of what the environment looked like at the time the function was defined.fold is like reduce.o: f1 o f2 o f3 - best to do a val binding to avoid unnecessary wrapping:
val newfunc = f1 o f2 - with
oyou apply functions from right to left sof1 o f2 xis the same asf1(f2(x))there is an alternative that is left to right called thepipeline operator.
- Universal way to make a func curryable:
ml fun myfunc x let fun f2 (z) = z fun f1 (y) = f2(y) begin f1 end - ML has first class support for currying so you don’t have to do the above hack.
let-rec in racket.Racket (Part B)
Racket is related to Lisp and Scheme. Everything is a function. Parenthesis for everything. The position of parenthesis changes the meaning of the code.
- Streams: the function will return a tuple of (value, func), and when you call func it will return (value, func) so you get one value at a time. This is not specific to Racket.
- Lazy evaluation: You can use thunks to delay execution like a promise to a later time. This is an example of lazy evalution that doesn’t actually evaluate anything until being forced to:
(define (my-delay f) (mcons #f f)) (define (my-force th) (if (mcar th) (mcdr th) (begin (set-mcar! th #t) (set-mcdr! th ((mcdr th))) (mcdr th))))
Racket allows you use macros that will evaluate before the code is run and that will “expand” into valid racket syntax.
You implemented your own small programming language. This used recursive calls to evluate expressions with the base case being the values (Integer, strings, etc). - Interperter: write a program in another language A that takes programs in B and produces answers directly. A better term would be “evaluator”. - Compiler: write program in another language A that takes programs in B and produces an equivalent program in langauage C. A better term here would be “translator”.
Closures: for lexical scope, the interpreter has a stack of tuples. The tuples are (1) the function to be called (2) the environment, which contains the value of all variables at the time the function was defined. You also have to track the arguments for the function seperately, so you can evaluate the arguments in the environment the function was run in.
Ruby (Part C)
I didn’t spend too much time some concepts I was mostly familiar with this.
Ruby is OOP, dynamically typed.
Ruby is pure OOP, even top level functions and variables are part of the built-in Object class.
They have fastcore like shortcuts for getters and setters:
attr_reader :y, :z # defines getters attr_accessor :x # defines getters and setters
newlines are important. The syntax can change without them.
Dynamic class definitions. The following code will result in Class with the methods foo and bar! The second one doesn’t override the first one!
class Class def foo ... end end class Class def bar ... end end
Blocks
They also have a very convenient lambda like thing called Blocks:
sum = 0 [4,6,8].each { |x| sum += x puts sum }
You can use Blocks to make accumulators too, and even use inject to initialize the accumulator:
sum = [4,6,8].inject(0) { |acc,elt| acc + elt }
To use blocks in a method, you will have to look that up in the docs. This involves the yield keyword. For example, this code will print “hi” 3 times:
def foo x if x yield else yield yield end end foo (true) { puts "hi" } foo (false) { puts "hi" }
Blocks are not first class functions even though they kind of look like lambdas. Lets say you wanted to map over an array but wanted to return an array of functions instead of values. The way to do this is to use the keyword lambda:
c = a.map {|x| {|y| x >= y} } # wrong, a syntax error c = a.map {|x| lambda {|y| x >= y} } # this will work
Subclassing
super calls the same method in the parent class. You dont have to do super.method_name(), just super.@Child classes are defined like this:
class Child < Parent ... end
Typing
They discussed the various ways different type systems are constructed. The interface idiom, that is familar to you from Golang (but not specific to Golang) was introduced.
VIM
For the Standard ML programming language I decided to force myself to use vim. I added the following things to my .vimrc to make it manageable. Note the plugin jez/vim-better-sml
" from https://github.com/jez/vim-as-an-ide set nocompatible inoremap <C-e> <C-o>A filetype off set rtp+=~/.vim/bundle/Vundle.vim call vundle#begin() Plugin 'VundleVim/Vundle.vim' " ----- Making Vim look good ------------------------------------------ Plugin 'altercation/vim-colors-solarized' Plugin 'tomasr/molokai' Plugin 'vim-airline/vim-airline' Plugin 'vim-airline/vim-airline-themes' " ----- Vim as a programmer's text editor ----------------------------- Plugin 'scrooloose/nerdtree' Plugin 'jistr/vim-nerdtree-tabs' Plugin 'vim-syntastic/syntastic' Plugin 'xolox/vim-misc' Plugin 'xolox/vim-easytags' Plugin 'majutsushi/tagbar' Plugin 'ctrlpvim/ctrlp.vim' " ----- Working with Git ---------------------------------------------- Plugin 'airblade/vim-gitgutter' Plugin 'tpope/vim-fugitive' Plugin 'Raimondi/delimitMate' Plugin 'jez/vim-better-sml' Plugin 'christoomey/vim-tmux-navigator' Plugin 'benmills/vimux' call vundle#end() filetype plugin indent on set number set ruler set showcmd set incsearch set hlsearch set backspace=indent,eol,start syntax on set mouse=a