Archive for August, 2014

Do-notation can be misleading

Wednesday, August 13th, 2014

Consider the following function:

oddness :: Maybe Int
oddness = do
  let a = Just 1 :: Maybe Int
  b <- a
  return b

Perfectly fine, albeit contrived, redundant, etc. Bear with me. Now consider what happens if we change the value of a:

oddness :: Maybe Int
oddness = do
  let a = Nothing :: Maybe Int
  b <- a
  return b

This looks odd, because it looks like we’re extracting the value from a into b, and then passing it to return – it looks like there’s some Int we extract from Nothing, and calling return converts it back to Nothing.

But of course, we know that this do-notation desugars to something like:

oddness :: Maybe Int
oddness =
  let a = Nothing :: Maybe Int
  in a >>= \b -> return b

And recall the definition of (>>=) for Maybe:

instance  Monad Maybe  where
    (Just x) >>= k      = k x
    Nothing  >>= _      = Nothing

So what’s happening here is that what’s on the right of (>>=) remains unevaluated, and Nothing is the result. Mystery solved.

C++ Guru Question – followup

Tuesday, August 12th, 2014

(following on from C++ Guru Question)

There are a few reasons why the code before didn’t work: mainly

a) C++ template argument deduction works one-way with a list of candidates, it’s not H-M type inference.
b) A C++ lambda is a thing with some internal type, not a std::function (although it can be assigned to one, that doesn’t matter for template type deduction).

Anyway, a reasonable solution to this problem is to simplify the template argument matching to the function type, and use a simple function_traits template to take apart the function argument and return types.

template <typename T>
struct function_traits
  : public function_traits<decltype(&T::operator())>
{};
 
template <typename R, typename A>
struct function_traits<R(A)>
{
  typedef R returnType;
  typedef A argType;
};
 
template <typename C, typename R, typename A>
struct function_traits<R(C::*)(A) const>
  : public function_traits<R(A)>
{};
 
template <typename T>
struct Foo
{
  T m_t;
};
 
template <typename F>
typename function_traits<F>::returnType operator/=(
    Foo<typename function_traits<F>::argType> foo, const F& fn)
{
  return fn(foo.m_t);
}
 
void mystery()
{
  auto foo = Foo<int>{1};
 
  // this works...
  function<Foo<int>(int)> f1 = [] (int i) { return Foo<int>{i}; };
  auto bar1 = foo /= f1;
 
  // this does too!
  auto f2 = [] (int i) { return Foo<int>{i}; };
  auto bar2 = foo /= f2;
}

C++ Guru Question

Monday, August 11th, 2014

Wondering about this… template argument deduction succeeds for the explicitly-typed variable, fails in the auto case. (Also, it succeeds either way for an equivalently-typed unary operator template).

template <typename T>
struct Foo
{
  T m_t;
};
 
template <typename T, typename U>
Foo<U> operator/=(Foo<T> foo, function<Foo<U>(T)> fn)
{
  return fn(foo.m_t);
}
 
void mystery()
{
  auto foo = Foo<int>{1};
 
  // this works...
  function<Foo<int>(int)> f1 = [] (int i) { return Foo<int>{i}; };
  auto bar1 = foo /= f1;
 
  // this doesn't
  auto f2 = [] (int i) { return Foo<int>{i}; };
  auto bar2 = foo /= f2;
 
  // clang++ 3.3-16ubuntu1 says:
  // file:line:col: error: no viable overloaded '/='
  // auto bar2 = foo /= f2;
  //             ~~~ ^  ~~
 
  // file:line:col: note: candidate template ignored: could not match
  // 'function<Foo<type-parameter-0-1> (type-parameter-0-0)>' against
  // '<lambda at file:line:col>'
  // Foo<U> operator/=(Foo<T> foo, std::function<Foo<U>(T)> fn)
  //        ^
}

(followup to this)