mostly-adequate-guide icon indicating copy to clipboard operation
mostly-adequate-guide copied to clipboard

Incorrect type signature of findParam in Chapter 8

Open NMinhNguyen opened this issue 7 years ago • 3 comments

I have a feeling that the type signature in an en example from Chapter 8 is incorrect:

//  params :: String -> [[String]]
var params = compose(toPairs, last, split('?'));

//  findParam :: String -> IO Maybe [String]
var findParam = function(key) {
  return map(compose(Maybe.of, filter(compose(eq(key), head)), params), url);
};

Since we're merely applying a filter to params, wouldn't the type be [[String]], and after applying Maybe.of, Maybe [[String]] instead of Maybe [String]?

NMinhNguyen avatar Aug 29 '16 20:08 NMinhNguyen

yeah, the name 'findParams' is misleading. It implies that the result is maybe just one thing (a pair). This would make the typensignature correct. But the implementation of this method uses 'filter' which is always returning a Collection. Therefore Minh is correct.

Sent from my Tricorder

On 29.08.2016, at 22:17, Minh Nguyen [email protected] wrote:

I have a feeling that the type signature in an en example from Chapter 8 is incorrect:

// params :: String -> [[String]] var params = compose(toPairs, last, split('?'));

// findParam :: String -> IO Maybe [String] var findParam = function(key) { return map(compose(Maybe.of, filter(compose(eq(key), head)), params), url); }; Since we're merely applying a filter to params, wouldn't the type be [[String]], and after applying Maybe.of, Maybe [[String]] instead of Maybe [String]?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.

snackycracky avatar Aug 29 '16 21:08 snackycracky

There's indeed an issue with the combination of those functions. In the end, it'd make more sense to rely on a find intermediate function with an appropriate signature.

// find :: (a -> Boolean) -> [a] -> Maybe a
const find = curry((p, xs) => compose(Maybe.of, head, filter(p))(xs));
// or
const find = curry((p, xs) => Maybe.of(xs.find(p)));

// findParam :: String -> IO (Maybe [String])
const findParam = key => { 
  const findOne = find(compose(eq(key), head));
  return map(compose(findOne, params), url);
};

We could even go further and quickly introduce / pull from the annexe a Pair type.

class Pair {
  static of(x) {
    return new Pair([x, x]); 
  }

  static fromList(xs) {
    return new Pair(xs); 
  }

  constructor(xs) {
    this.$value = xs; 
  }

  inspect() {
    return `Pair(${inspect(this.fst())}, ${inspect(this.snd())})`; 
  }

  fst() {
    return this.$value[0];
  }

  snd() {
    return this.$value[1]; 
  }
}

// fst :: Pair a -> a
const fst = p => p.fst();

// snd :: Pair a -> a
const snd = p => p.snd();

which leads to more comprehensive / richer type signatures:

// toPairs :: String -> [Pair String]
const toPairs = compose(map(compose(Pair.fromList, split('='))), split('&'));

// params :: String -> [Pair String]
const params = compose(toPairs, last, split('?'));

// find :: (a -> Boolean) -> [a] -> Maybe a
const find = curry((p, xs) => Maybe.of(xs.find(p)));

// findParam :: String -> IO (Maybe(Pair String))
const findParam = key => {
  const findOne = find(compose(eq(key), fst));
  return map(compose(findOne, params), url);
};

KtorZ avatar Jan 21 '18 12:01 KtorZ

I believe only the signature should be corrected as initially suggested, and that the implementation should be left alone. Since a url param key is allowed to be duplicated (and there are use cases for such), findParam should result in Maybe [[String]] in order to collect all pairs with the same key.

chuckwondo avatar Dec 21 '19 12:12 chuckwondo