I love using Haskell but am forced to use C++ for school assignments. I am writing my own library for C++ that emulates Haskell's Prelude functions so I can write in a more concise and functional style in C++ if I wish (repo on GitHub).
A problem I have run into is implementing functions like map
that operate on lists. In Haskell, String
is equivalent to [Char]
, so you can use strings in functions that take lists. In C++, std::string
is not the same thing as std::vector<char>
, so I have to write multiple versions of functions to take std::string
or std::vector<Type>
. This works fine for functions like filter
or tail
because their input and output are the same type. But with map
, I need to be able to transform a vector of int
s into char
s, or a string
into a vector of bool
s.
When trying to run a simple pig latin converter (pigLatin.cpp
on GitHub), the unwords
function fails because map
isn't working.
examples/pigLatin.cpp:20:29: error: no matching function for call to 'unwords'
std::string translation = unwords(map(pigLatin, words(input)));
^~~~~~~
examples/../prelude.hpp:591:15: note: candidate function not viable: no known conversion from 'std::string' (aka 'basic_string<char, char_traits<char>, allocator<char> >') to 'const std::vector<std::string>'
(aka 'const vector<basic_string<char, char_traits<char>, allocator<char> > >') for 1st argument
std::string unwords(const std::vector<std::string> &xs) {
^
1 error generated.
How can I write my map
function such that it behaves like the one in Haskell (map
on Hackage):
map :: (a -> b) -> [a] -> [b]
I don't know enough about the nuances of C++ templates to figure this one out. This is what I have so far (map
from prelude.hpp
on GitHub):
// map :: (a -> b) -> [a] -> [b]
template <typename Function, typename Input, typename Output>
std::vector<Output> map(const Function &f, const std::vector<Input> &xs) {
const int size = xs.size();
std::vector<Output> temp;
for (int i = 0; i < size; ++i) {
temp.push_back(f(xs[i]));
}
return temp;
}
// map :: (String -> a) -> String -> [a]
template <typename Function, typename Output>
std::vector<Output> map(const Function &f, const std::string &xs) {
const int size = xs.size();
std::vector<Output> temp;
for (int i = 0; i < size; ++i) {
temp.push_back(f(xs[i]));
}
return temp;
}
// map :: (a -> String) -> [a] -> String
template <typename Function, typename Input>
std::string map(const Function &f, const std::vector<Input> &xs) {
const int size = xs.size();
std::string temp;
for (int i = 0; i < size; ++i) {
temp += f(xs[i]);
}
return temp;
}
// map :: (String -> String) -> String -> String
template <typename Function>
std::string map(const Function &f, const std::string &xs) {
const int size = xs.size();
std::string temp;
for (int i = 0; i < size; ++i) {
temp += f(xs[i]);
}
return temp;
}