Study Notes: Mostly adequate guide to FP: Example Flick App

875 words · 5 min read

ch06: Flickr App Example

v01: Basics

Source code in https://github.com/mertnuhoglu/study/js/ex/study_frisby_flickr_app_ex01/

HTML page:

cat "ex/study_frisby_flickr_app_ex01/index.html" 
## <!doctype html>
## <html lang="en">
##   <head>
##     <meta charset="utf-8">
##     <title>Flickr Demo</title>
##   </head>
##   <body>
##     <main id="js-main" class="main"></main>
##     <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.2.0/require.min.js"></script>
##     <script src="index.js"></script>
##   </body>
## </html>
cat "ex/study_frisby_flickr_app_ex01/index.js" 
## const CDN = s => `https://cdnjs.cloudflare.com/ajax/libs/${s}`;
## const ramda = CDN('ramda/0.21.0/ramda.min');
## const jquery = CDN('jquery/3.0.0-rc1/jquery.min');
## 
## requirejs.config({ paths: { ramda, jquery } });
## require(['jquery', 'ramda'], ($, { compose, curry, map, prop }) => {
##    const Impure = {
##     getJSON: curry((callback, url) => $.getJSON(url, callback)),
##     setHtml: curry((sel, html) => $(sel).html(html)),
##     trace: curry((tag, x) => { console.log(tag, x); return x; }),
##   }; 
##   const app = Impure.setHtml('body');
##   app('cats');
## });

The resulting web page embedded as iframe:

mnemonics

Assume that:

const f = curry( (x,y) => ... )

Then first call puts single argument into the first position. Second call puts the argument into the next position:

f(x)(y)

Assume that:

const Impure = {
  getJSON: curry((callback, url) => $.getJSON(url, callback)),
  setHtml: curry((sel, html) => $(sel).html(html)),
  trace: curry((tag, x) => { console.log(tag, x); return x; }),
}; 
const app = compose(Impure.getJSON(Impure.trace('response')), url);

Consider:

Impure.getJSON(Impure.trace('response'))

Type signatures of the curried functions are:

Impure.trace('response') :: a -> a
Impure.getJSON :: (a->a) -> a -> j
Impure.getJSON(Impure.trace('response')) :: a -> j

v02: trace()

Source code in https://github.com/mertnuhoglu/study/js/ex/study_frisby_flickr_app_ex02/

Now, let’s do some http requests and embed the resulting json data into web page.

const host = 'api.flickr.com';
const path = '/services/feeds/photos_public.gne';
const query = t => `?tags=${t}&format=json&jsoncallback=?`;
const url = t => `https://${host}${path}${query(t)}`;
const app = compose(Impure.getJSON(Impure.trace('response')), url);
app('cats');

This code prints the result of trace('response', url('cats'))

## response {
##   "title": "Recent Uploads tagged cats",
##   "link": "https://www.flickr.com/photos/tags/cats/",
##   "description": "",
##   "modified": "2018-03-08T19:13:09Z",
##   "generator": "https://www.flickr.com",
##   "items": [
##     {
##       "title": "Watch high quality movies at http://ImovieSh.com/",
##       "link": "https://www.flickr.com/photos/imoviesh/39985324204/",
##       "media": {
##         "m": "https://farm5.staticflickr.com/4785/39985324204_0124bb7b0b_m.jpg"
##       },
##       "date_taken": "2018-03-08T11:13:09-08:00",
##       "description": " <p><a href=\"https://www.flickr.com/people/imoviesh/\">ImovieSh</a> posted a photo:</p> <p><a href=\"https://www.flickr.com/photos/imoviesh/39985324204/\" title=\"Watch high quality movies at http://ImovieSh.com/\"><img src=\"https://farm5.staticflickr.com/4785/39985324204_0124bb7b0b_m.jpg\" width=\"185\" height=\"240\" alt=\"Watch high quality movies at http://ImovieSh.com/\" /></a></p> <p>Watch high quality movies at <a href=\"http://ImovieSh.com/\" rel=\"nofollow\">ImovieSh.com/</a></p>",
##       "published": "2018-03-08T19:13:09Z",
##       "author": "nobody@flickr.com (\"ImovieSh\")",
##       "author_id": "160658215@N07",
##       "tags": "cute dogs animals aww cats love girls movie animal images baby birds adorable"
##     },
##  ...

Result in:

https://rawgit.com/mertnuhoglu/study/master/js/ex/study_frisby_flickr_app_ex02/index.html

v03: Use ES Modules with ParcelJs Bundler

Source code in https://github.com/mertnuhoglu/study/js/ex/study_frisby_flickr_app_ex03/

Let’s move this code into a ParcelJs app:

mkdir study_frisby_flickr_app_ex03 && cd $_ && npm init -y && npm i parcel-bundler jquery ramda
cp ../study_frisby_flickr_app_ex02/index.html . && mkdir -p src && cp ../study_frisby_flickr_app_ex02/index.js .

index.js imports jquery and ramda

const jquery = require("jquery")
window.$ = window.jQuery = jquery;
const R = require('ramda');

package.json tells to use parcel as package bundler:

...
"start": "parcel index.html",
"build": "parcel build index.html --public-url ./",

Run npm start and open http://localhost:1234

Or open from rawgit: https://rawgit.com/mertnuhoglu/study/master/js/ex/study_frisby_flickr_app_ex03/dist/index.html

cat "ex/study_frisby_flickr_app_ex03/index.html" 
## <!doctype html>
## <html lang="en">
##   <head>
##     <meta charset="utf-8">
##     <title>Flickr Demo</title>
##   </head>
##   <body>
##     <main id="js-main" class="main"></main>
##     <script src="index.js"></script>
##   </body>
## </html>
cat "ex/study_frisby_flickr_app_ex03/index.js" 
## const jquery = require("jquery")
## window.$ = window.jQuery = jquery;
## const R = require('ramda');
## 
## const Impure = {
##   getJSON: R.curry((callback, url) => $.getJSON(url, callback)),
##   setHtml: R.curry((sel, html) => $(sel).html(html)),
##   trace: R.curry((tag, x) => { console.log(tag, JSON.stringify(x, null, 2)); return x; }),
## }; 
## const host = 'api.flickr.com';
## const path = '/services/feeds/photos_public.gne';
## const query = t => `?tags=${t}&format=json&jsoncallback=?`;
## const url = t => `https://${host}${path}${query(t)}`;
## const app = R.compose(Impure.getJSON(Impure.trace('response')), url);
## app('cats');

v04: DOM

Source code in https://github.com/mertnuhoglu/study/js/ex/study_frisby_flickr_app_ex04/

mkdir study_frisby_flickr_app_ex04 && cd $_ && npm init -y && npm i parcel-bundler jquery ramda
cp ../study_frisby_flickr_app_ex03/index.html . && mkdir -p src && cp ../study_frisby_flickr_app_ex03/index.js .

We will construct img elements out of this json data.

...
const mediaUrl = compose(prop('m'), prop('media'));
const mediaUrls = compose(map(mediaUrl), prop('items'));
const render = compose(Impure.setHtml('js-main'), mediaUrls);
const app = compose(Impure.getJSON(render), url);

This code collects all m properties inside response.items[].media elements.

v05: Testing from REPL

Source code in https://github.com/mertnuhoglu/study/js/ex/study_frisby_flickr_app_ex05/

Testing through HTML pages is sometimes difficult. I want to check if I can test the above functions using Node REPL.

mkdir -p ex/study_frisby_flickr_app_ex05 && cd $_ && npm init -y && pnpm i parcel-bundler ramda request request-promise-native

Edit ex/study_frisby_flickr_app_ex05/index.js:

var request = require('request-promise-native');
var {compose, curry} = require('ramda');

var Impure = {
  getJSON: curry((callback, url) => 
    request(url).then(callback).
    catch( err => console.log('error:', err)) 
  ),
  trace: curry((tag, x) => { console.log(x); return x; }),
}; 

I replaced jquery with request library because it can be run directly on node’s REPL. That is easier to test in than in a browser.

node ex/study_frisby_flickr_app_ex05/src/index.js | sed -n '1,6 p'
## module.js:545
##     throw err;
##     ^
## 
## Error: Cannot find module '/Users/mertnuhoglu/projects/jekyll/mertnuhoglu.com/content/tech/ex/study_frisby_flickr_app_ex05/src/index.js'
##     at Function.Module._resolveFilename (module.js:543:15)
##     at Function.Module._load (module.js:470:25)
##     at Function.Module.runMain (module.js:690:10)
##     at startup (bootstrap_node.js:194:16)
##     at bootstrap_node.js:666:3

v06: DOM Again

We will construct img elements out of the json data, but this time we will use request library.

...
const mediaUrl = compose(prop('m'), prop('media'));
const mediaUrls = compose(map(mediaUrl), prop('items'));
const render = compose(Impure.setHtml('js-main'), mediaUrls);
const app = compose(Impure.getJSON(render), url);

This code collects all m properties inside response.items[].media elements.

 Tech    16 Feb, 2018

Any work (images, writings, presentations, ideas or whatever) which I own is always provided under
Creative Commons License Creative Commons Attribution-Share Alike 3.0 License

Mert Nuhoglu is a Trabzon-born programmer and data scientist.

You may also like...