Affordances in Programming Languages

Post on 26-Dec-2014

855 views 1 download

description

From Mountain West Ruby Conference 2014. A good design communicates the intended use of an object. In the physical world, this communication is accomplished by “affordances”, as discussed by Donald Norman in “The Psychology of Everyday Things”. Programming languages also have affordances, and they influence the kinds of solutions we develop. The more languages we know, the more we “expand our design space” so that we can come up with better solutions to the problems we face every day.

Transcript of Affordances in Programming Languages

3/20/2014 Affordances

http://localhost:9090/onepage 1/67

Affordances inProgramming Languages

Randy CoulmanPrincipal Software Engineer

http://randycoulman.com

 @randycoulman

 randycoulman

3/20/2014 Affordances

http://localhost:9090/onepage 2/67

Satu Kyröläinen ­ http://www.satukyrolainen.com/affordances/

3/20/2014 Affordances

http://localhost:9090/onepage 3/67

Satu Kyröläinen ­ http://www.satukyrolainen.com/affordances/

3/20/2014 Affordances

http://localhost:9090/onepage 4/67

Affordance

A quality of an object or environment that allows someone toperform an action.

3/20/2014 Affordances

http://localhost:9090/onepage 5/67

Nicolas Nova ­ http://www.flickr.com/photos/nnova/2252222949/

3/20/2014 Affordances

http://localhost:9090/onepage 6/67

William Lindeke ­ http://tcsidewalks.blogspot.com/2009/06/signs­of­times­14.html

3/20/2014 Affordances

http://localhost:9090/onepage 7/67

Yoni Alter ­ http://www.yoniishappy.com/Tube­escalators

3/20/2014 Affordances

http://localhost:9090/onepage 8/67

Affordances and Software

3/20/2014 Affordances

http://localhost:9090/onepage 9/67

Amir Rajan (@amirrajan)

Coding Out Loud ­ REST APIs series

http://vimeo.com/channels/659338

3/20/2014 Affordances

http://localhost:9090/onepage 10/67

ExamplePoints

Smalltalk ­> Ruby

3/20/2014 Affordances

http://localhost:9090/onepage 11/67

class Point def initialize(x, y) @x = x @y = y endend

Point.new(3, 4)

3/20/2014 Affordances

http://localhost:9090/onepage 12/67

http://images.cryhavok.org/d/25562­2/Polar+and+Cartesian+Bears.jpg

3/20/2014 Affordances

http://localhost:9090/onepage 13/67

θ

r sinθ

r cosθ

r

x

y

http://upload.wikimedia.org/wikipedia/commons/7/78/Polar_to_cartesian.svg

3/20/2014 Affordances

http://localhost:9090/onepage 14/67

class Point def initialize(x, y) @x = x @y = y endend

Point.new(3, 4)

3/20/2014 Affordances

http://localhost:9090/onepage 15/67

Point class>>x: anX y: aY ̂self new initializeX: anX y: aY

Point>>initializeX: anX y: aY x := anX. y := aY

Point x: 3 y: 4

3/20/2014 Affordances

http://localhost:9090/onepage 16/67

Point class>> r: radius theta: angleInRadians ̂self x: radius * angleInRadians cos y: radius * angleInRadians sin

Point r: 5 theta: 0.927295

3/20/2014 Affordances

http://localhost:9090/onepage 17/67

Affordance

Named Constructors

3/20/2014 Affordances

http://localhost:9090/onepage 18/67

class Point def self.xy(x, y) new(x, y) end

def self.polar(r, theta) xy(r * Math.cos(theta), r * Math.sin(theta)) end

private_class_method :new

# ... rest same as beforeend

Point.xy(3, 4)Point.polar(5, 0.927295)

3/20/2014 Affordances

http://localhost:9090/onepage 19/67

ExampleFind/DetectSmalltalk ­> Ruby

3/20/2014 Affordances

http://localhost:9090/onepage 20/67

#(2 4 6 8) detect: [:each | each odd]

"Unhandled exception: Element Not Found"

3/20/2014 Affordances

http://localhost:9090/onepage 21/67

[2, 4, 6, 8].find { |n| n.odd? }

# => nil

3/20/2014 Affordances

http://localhost:9090/onepage 22/67

#(2 4 6 8) detect: [:each | each odd] ifNone: [#none]

"#none"

3/20/2014 Affordances

http://localhost:9090/onepage 23/67

Affordance

Multiple Blocks

3/20/2014 Affordances

http://localhost:9090/onepage 24/67

[2, 4, 6, 8].find(-> {:none}) { |n| n.odd?} # => :none

3/20/2014 Affordances

http://localhost:9090/onepage 25/67

Linguistic Relativitya.k.a. The Sapir­Whorf Hypothesis

"[T]he structure of a language affects the ways in which itsrespective speakers conceptualize their world ... or otherwiseinfluences their cognitive processes."

­­ http://en.wikipedia.org/wiki/Linguistic_relativity

3/20/2014 Affordances

http://localhost:9090/onepage 26/67

When Code CriesCory Foy at SCNA 2012

http://vimeo.com/53986875

What does a language allow you to say?

What does a language force you to say?

3/20/2014 Affordances

http://localhost:9090/onepage 27/67

The Power and Philsophy of RubyMatz at OSCON 2003

http://www.rubyist.net/~matz/slides/oscon2003/mgp00001.html

"Languages are not only tools to communicate, but also toolsto think."

3/20/2014 Affordances

http://localhost:9090/onepage 28/67

ExampleCleaning Up After Yourself

C++ ­> Ruby

3/20/2014 Affordances

http://localhost:9090/onepage 29/67

if ((err = SSLFreeBuffer(&hashCtx)) != 0) goto fail;if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) != 0) goto fail;if ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != goto fail;if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != goto fail;if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != goto fail; goto fail;if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0) goto fail;

//...

fail: SSLFreeBuffer(&signedHashes); SSLFreeBuffer(&hashCtx); return err;

3/20/2014 Affordances

http://localhost:9090/onepage 30/67

#include "support.h"#include <iostream>

void foo(){ Resource* resource = acquireResource(); bar(resource);

if (baz(resource) != 42) return;

std::cout << "Completed successfully!" << std::endl;

releaseResource(resource);}

3/20/2014 Affordances

http://localhost:9090/onepage 31/67

$ ./brokenAcquiring resourceCaught exception: oops!

3/20/2014 Affordances

http://localhost:9090/onepage 32/67

#include "support.h"#include <iostream>

void foo(){ Resource* resource = acquireResource();

try { bar(resource);

if (baz(resource) == 42) { std::cout << "Completed successfully!" << std::endl; } } catch(std::exception& e) { releaseResource(resource); throw; }

releaseResource(resource);}

3/20/2014 Affordances

http://localhost:9090/onepage 33/67

$ ./wordyAcquiring resourceReleasing resourceCaught exception: oops!

3/20/2014 Affordances

http://localhost:9090/onepage 34/67

#include "support.h"#include <iostream>

void foo(){ Resource* resource = acquireResource();

try { bar(resource);

if (baz(resource) == 42) { std::cout << "Completed successfully!" << std::endl; } } catch(std::exception& e) { releaseResource(resource); throw; }

releaseResource(resource);}

3/20/2014 Affordances

http://localhost:9090/onepage 35/67

3/20/2014 Affordances

http://localhost:9090/onepage 36/67

RAIIResource Acquisition Is Initialization

Acquire resources in the constructor

Release them in the destructor

3/20/2014 Affordances

http://localhost:9090/onepage 37/67

#include "SafeResource.h"#include "support.h"

SafeResource::SafeResource() : resource(acquireResource()){}

SafeResource::~SafeResource(){ releaseResource(resource);}

Resource* SafeResource::get(){ return resource;}

3/20/2014 Affordances

http://localhost:9090/onepage 38/67

#include "SafeResource.h"#include "support.h"#include <iostream>

void foo(){ SafeResource resource; bar(resource.get());

if (baz(resource.get()) != 42) return

std::cout << "Completed successfully!" << std::endl;}

3/20/2014 Affordances

http://localhost:9090/onepage 39/67

$ ./raiiAcquiring resourceReleasing resourceCaught exception: oops!

3/20/2014 Affordances

http://localhost:9090/onepage 40/67

Affordance

Deterministic Destructors

3/20/2014 Affordances

http://localhost:9090/onepage 41/67

Ruby doesn't have deterministic destructors.So what can we do instead?

3/20/2014 Affordances

http://localhost:9090/onepage 42/67

Blocks!

3/20/2014 Affordances

http://localhost:9090/onepage 43/67

class SafeResource def self.acquire resource = self.new yield resource ensure resource.release end

def initialize puts "Acquiring resource" @resource = Object.new end

def release puts "Releasing resource" @resource = nil end

3/20/2014 Affordances

http://localhost:9090/onepage 44/67

require_relative "support"require_relative "safe_resource"

def foo SafeResource.acquire do |resource| bar(resource)

return unless baz(resource) == 42

puts "Completed successfully!" endend

3/20/2014 Affordances

http://localhost:9090/onepage 45/67

$ ruby driver.rbAcquiring resourceReleasing resourceCaught exception: oops!

3/20/2014 Affordances

http://localhost:9090/onepage 46/67

ExampleImageReaderSmalltalk ­> Ruby

3/20/2014 Affordances

http://localhost:9090/onepage 47/67

ImageReader class>>fromFile: aFilename | readerClass imageStream reader |

imageStream := aFilename readStream binary. [readerClass := self readerClassFor: imageStream. readerClass ifNil: [̂self error: 'Unknown image type: ', aFilename asString]. reader := readerClass on: imageStream] ensure: [imageStream close]. ̂reader

3/20/2014 Affordances

http://localhost:9090/onepage 48/67

ImageReader class>>readerClassFor: imageStream ̂self subclasses detect: [:eachClass | imageStream reset. [eachClass canRead: imageStream] ensure: [imageStream reset]] ifNone: [nil]

3/20/2014 Affordances

http://localhost:9090/onepage 49/67

Affordance

Subclass Iteration

3/20/2014 Affordances

http://localhost:9090/onepage 50/67

class ImageReader

def self.read(filename) File.open(filename, "rb") do |io| reader_class = find_reader_class(io) raise "Unknown image type: #{filename}" unless reader_class reader_class.new(io) end end

#...end

3/20/2014 Affordances

http://localhost:9090/onepage 51/67

class ImageReader #...

def self.find_reader_class(io) subclasses.find { |reader| begin io.rewind reader.can_read?(io) ensure io.rewind end } end

#...end

3/20/2014 Affordances

http://localhost:9090/onepage 52/67

class ImageReader #...

def self.inherited(subclass) subclasses << subclass end

def self.subclasses @subclasses ||= [] end

#...end

3/20/2014 Affordances

http://localhost:9090/onepage 53/67

class BMPImageReader < ImageReader def self.can_read?(io) io.read(2) == "BM" end

def read_image puts "Reading BMP" endend

3/20/2014 Affordances

http://localhost:9090/onepage 54/67

class JPGImageReader < ImageReader def self.can_read?(io) io.read(2) == "\xFF\xD8".b end

def read_image puts "Reading JPG" endend

3/20/2014 Affordances

http://localhost:9090/onepage 55/67

class PNGImageReader < ImageReader def self.can_read?(io) io.read(8) == "\x89PNG\r\n\x1A\n".b end

def read_image puts "Reading PNG" endend

3/20/2014 Affordances

http://localhost:9090/onepage 56/67

require_relative "image_readers"

%w{bmp jpg png}.each do |ext| ImageReader.read("example.#{ext}")end

3/20/2014 Affordances

http://localhost:9090/onepage 57/67

$ ruby subclasses.rbReading BMPReading JPGReading PNG

3/20/2014 Affordances

http://localhost:9090/onepage 58/67

Takeaways

3/20/2014 Affordances

http://localhost:9090/onepage 59/67

Languages afford certain designsand inhibit others

3/20/2014 Affordances

http://localhost:9090/onepage 60/67

Languages influence thought

3/20/2014 Affordances

http://localhost:9090/onepage 61/67

Learning new languages willincrease your "solution space"

3/20/2014 Affordances

http://localhost:9090/onepage 62/67

Don't go too far

3/20/2014 Affordances

http://localhost:9090/onepage 63/67

Want More?http://randycoulman.com/blog/categories/affordances/

3/20/2014 Affordances

http://localhost:9090/onepage 64/67

AcknowledgementsKey Technology (http://www.key.net/)

Rogue.rb (@roguerb)

Jen Myers (@antiheroine)

3/20/2014 Affordances

http://localhost:9090/onepage 65/67

ReferencesThe Design of Every Day Things ­ Donald Norman

Coding Out Loud ­ Amir Rajan

Linguistic Relativity ­ Wikipedia article

When Code Cries ­ Cory Foy at SCNA 2012

The Power and Philosophy of Ruby ­ Matz at OSCON 2003

3/20/2014 Affordances

http://localhost:9090/onepage 66/67

Questions?

3/20/2014 Affordances

http://localhost:9090/onepage 67/67

Randy Coulmanhttp://speakerrate.com/randycoulman

http://www.slideshare.net/randycoulmanhttp://randycoulman.com

 @randycoulman

 randycoulman