This question already has answers here:
Conversion of a slice of string into a slice of custom type
(3 answers)
Closed 3 months ago.
I'm trying to do something along these lines:
package main
import (
"fmt"
)
type StringWrap string
func main() {
s := []string{"a","b","c"}
sw := []StringWrap(s) //ERROR: cannot convert s (type []string) to type []StringWrap
fmt.Println(sw)
}
Am I doing something wrong? Or is this simply a limitation in go?
The Go Programming Language Specification
Types
A type determines the set of values and operations specific to values
of that type. A type may be specified by a (possibly qualified) type
name or a type literal, which composes a new type from previously
declared types.
Type = TypeName | TypeLit | "(" Type ")" .
TypeName = identifier | QualifiedIdent .
TypeLit = ArrayType | StructType | PointerType | FunctionType |
InterfaceType | SliceType | MapType | ChannelType .
Named instances of the boolean, numeric, and string types are
predeclared. Composite types—array, struct, pointer, function,
interface, slice, map, and channel types—may be constructed using type
literals.
Each type T has an underlying type: If T is a predeclared type or a
type literal, the corresponding underlying type is T itself.
Otherwise, T's underlying type is the underlying type of the type to
which T refers in its type declaration.
type T1 string
type T2 T1
type T3 []T1
type T4 T3
The underlying type of string, T1, and T2 is string. The underlying
type of []T1, T3, and T4 is []T1.
Conversions
Conversions are expressions of the form T(x) where T is a type and
x is an expression that can be converted to type T.
A non-constant value x can be converted to type T in the case:
x's type and T have identical underlying types.
For example,
package main
import "fmt"
type StringSliceWrap []string
func main() {
s := []string{"a", "b", "c"}
ssw := StringSliceWrap(s)
fmt.Println(ssw)
}
Output:
[a b c]
Related
when I write a type that e.g. only accepts strings: let type t1 = string
I can do let name : t1 = "A", but not let age : t1 = 1
But when I want to have a generic type that accepts any data type I have to do this: let type 'a t2 = 'a So I can do both let name : t2 = "A", let age : t2 = 1.
But why do I have to write let type 'a t2 = 'a instead of let type t2 = 'a?
The form
let type t1 = string
is not syntactically valid OCaml.
I imagine that you meant:
type t1 = string
Similarly, with the type constructor t2 defined as
type 'a t2 = 'a
then
let x : t2 = "hi"
is a type error because t2 is not a type but a type constructor of arity one.
The closest valid definition would be:
let x: string t2 = "hi"
which is equivalent
let x: 'a t2 = "hi"
because the type variable 'a is equated to 'a = string when inferring the type of x. But 'a t2 is an abbreviation for 'a, thus the above is still the same as
let x : string = "hi"
At a higher level, there is no useful generic type that accepts any data¹. Indeed, if it existed such type would break the type system.
¹ There are an advanced feature (GADTs or record with polymorphic fields) that allows to either create black-hole types that can carry a data of any kind but forbids any use of the data or types without any values of this type. However, it is probably better to first familiarize yourself with the core part of the type system before exploring those area.
To add to #octachron's answer, you can think of a declaration like type 'a t = 'a as the declaration of a function over types: given a type 'a, t will produce a new type 'a t (which in this case is just 'a). So you need the 'a just like you would need to declare the parameters in a function.
This shouldn't be confused with the occurrences of 'a in an explicit typing form like expr : 'a t. In this case, OCaml thinks of 'a as a type variable, which may or may not denote polymorphism. If you actually wanted to have polymorphism as is the polymorphic type, you'd have to introduce a polymorphic type variable either with 'a. ... or with a type a:
let id : 'a. 'a -> 'a = fun x -> x
let id (type a) (x : a) : a = x
However, note that these forms are not quite equivalent, there are subtle differences, and they are in any case different (in general, not in this specific example) from let id : 'a -> 'a = fun x -> x or let id (x : 'a) : 'a = x.
Which is better:
decltype((foo)) x = bar;
const decltype(foo)& x = bar;
If these are just two ways of saying the same thing, why are the parentheses even part of the language?
If you want to declare "a reference to the same type of object as foo", then I would prefer the second option. That's not really what the other form of decltype is for.
The decltype keyword pulls double-duty:
It tells you the type of a variable
It tells you the type and value category of an expression
That's what the extra parenthesis do. foo is an identifier, so decltype(foo) will tell you the type of the object identified by that identifier. (foo) is not an identifier; it is an expression, so decltype((foo)) will tell you both the type and value category of it. It returns an lvalue-reference for an lvalue expression, an rvalue-reference for an xvalue, and a non-reference type for a prvalue.
For example:
int i = 10;
using A = decltype(i); // A is an alias for int
// because i is an identifer
using B = decltype((i)); // B is an alias for int&
// because (i) is an lvalue expression
using C = decltype(std::move(i)); // C is an alias for int&&
// because std::move(i) is an xvalue
// expression
using D = decltype(i + 1); // D is an alias for int
// because i + 1 is a prvalue expression
Demo
The value categories of all of the expressions in that example are obvious from looking at them, but consider a template:
template <typename Func>
void foo(Func func)
{
func(42);
}
The expression func(42) could result in any value category, depending on the exact return type. That's where the expression form of decltype becomes useful.
This question already has answers here:
What expressions yield a reference type when decltype is applied to them?
(2 answers)
Closed 1 year ago.
I'm so confused about this template I wrote, It's supposed to 'auto' deduce a pointer type that I pass in, but the compiler doesn't seem to think that two types are the same, whereas I do, and typeid() is on my side.
#include <iostream>
template <auto* static_ptr_to_object>
struct Handle
{
// static_ptr_to_object should be a pointer to a Whale<char>
// Both of the following typedefs should result in types of Whale<char>
// When I cout typeid() of these types it prints out the same types
// However std::is_same_v returns false
using pointee_type1 = std::remove_pointer_t<decltype(static_ptr_to_object)>;
using pointee_type2 = decltype(*static_ptr_to_object); // The two are the same types
void showTypes()
{
//static_assert(std::is_same_v<pointee_type1, pointee_type2>);
// Uncommenting this line will make the static assert fail
std::cout << "Type of 'pointee_type1' = " << typeid(pointee_type1).name() << "\n";
std::cout << "Type of 'pointee_type2' = " << typeid(pointee_type2).name() << "\n";
if (typeid(pointee_type1) == typeid(pointee_type2))
std::cout << "Types are the same\n";
else std::cout << "Types are not the same\n";
}
bool b1 = std::is_integral_v<decltype(pointee_type1::member)>;
// Uncommenting the below line will make compilation fail
//bool b2 = std::is_integral_v<decltype(pointee_type2::member)>;
// pointee_type2 must be a class or namespace when followed by ::
// pointee_type2 left of '::' must be a class or namespace name
};
template <typename T>
struct Whale
{
T member;
};
Whale<char> whale;
int main()
{
Handle<&whale> handleToWhale;
handleToWhale.showTypes();
}
So I think the two types are the same, and typeid() operator says they are the same, yet std::is_same returns false, and based on the compiler error message it doesn't recognise that pointee_type2 is a class type because it doesn't recognise the :: operator for it.
Edit: My understanding about dereferencing pointers was wrong, this question relates to how a pointer dereference will return a reference type in some cases.
For decltype,
If the argument is an unparenthesized id-expression or an unparenthesized class member access expression, then decltype yields the type of the entity named by this expression. ......
If the argument is any other expression of type T, and
a) ...
b) if the value category of expression is lvalue, then decltype yields T&;
c) ...
*static_ptr_to_object fits the 2nd case, and it's an lvalue expression, so decltype(*static_ptr_to_object) yields a reference type, i.e. Whale<char>&, pointee_type1 and pointee_type2 are not the same type.
You can use std::remove_reference to get the same type as:
using pointee_type2 = std::remove_reference_t<decltype(*static_ptr_to_object)>;
On the other hand, typeid does yield same result (the same std::type_info object).
If type is a reference type, the result refers to a std::type_info object representing the referenced type.
pointee_type1 is Whale<char>, but pointee_type2 is Whale<char> &. typeid removes the reference, so you get the same name.
This question already has an answer here:
hana::second can't deduce type
(1 answer)
Closed 5 years ago.
I have a template class which accepts a tuple of pairs of a type and an integral constant (some of the types can be repeated so it can't be a hana::map). I'm looking to iterate over the tuple and call a static toString() type method defined for each type. The error I am receiving is:
"error: type 'decltype(hana::first(c))' (aka 'boost::hana::type_impl::_ &') cannot be used prior to '::' because it has no members"
struct A
{
static std::string toString() {return std::string("A");}
};
struct B
{
static std::string toString() {return std::string("B");}
};
using namespace hana::literals;
std::array<std::string,3> ret;
constexpr auto tupleOfPairs = hana::make_tuple(
hana::make_pair(hana::type_c<A>, 0_c),
hana::make_pair(hana::type_c<B>, 0_c),
hana::make_pair(hana::type_c<B>, 5_c));
size_t idx = 0;
hana::for_each(tupleOfPairs, [&](auto c)
{
ret[idx++] = decltype(hana::first(c))::type::toString();
});
I had something very similar working when it was just a tuple (using decltype(c)::type::toString()) but as soon as I made the tuple elements pairs with an integral constant I can't seem to extract the type of the first element of the pair and do the same.
Thanks
TL;DNR: That decltype yields a type "reference to ...", which (it's a reference) obviously has no members. Simple fix: remove the reference with std::remove_reference:
ret[idx++] =
std::remove_reference_t<decltype(hana::first(c))>::type::toString();
This is not about hana. Taking it "out of the loop" we can reduce your case further to
#include <string>
struct A {
static std::string toString() {
return std::string("A");
}
};
template<typename T> T & get_it() {
static T thing;
return thing;
}
int main() {
std::string str = decltype(get_it<A>())::toString();
}
This gives (basically) the same error message as yours:
error: 'decltype(get_it())' (aka 'A &') is not a class, namespace, or enumeration
This is telling us that A & (a reference to A) is not a class (etc.) and can thus not have a (static) member. The same is true when your compiler tells you that:
error: type 'decltype(hana::first(c))' (aka 'boost::hana::type_impl::_ &') cannot be used prior to '::' because it has no members
boost::hana::type_impl::_ & is a reference. Of course it has no members. The referenced class has, so just remove the reference with std::remove_reference.
The fact that the return type of first is reference type is also noted in the documentation, btw (emphasis mine):
Returns the first element of a pair.Note that if the Product actually stores the elements it contains, hana::first is required to return a lvalue reference, a lvalue reference to const or a rvalue reference to the first element, where the type of reference must match that of the pair passed to first. If [.., not applicable here].
Why can't I coerce record types in OCaml? Base types like int works fine.
Below is an example where I construct a base module M which I include in module A. M.t is type abbriviated in A. As long as M.t is int, I can do A.t' :> M.t. When I change it to {i : int}, the compiler says it's not a subtype. I'm guessing there is a very specific reason for this?
module M = struct
type t = {i : int}
let make () = {i = 10}
end
module A : sig
include module type of M
type t' = private t
val make : unit -> t'
end = struct
include M
type t' = t
end
In the toplevel:
(A.make() :> M.t);;
Error: Type A.t' is not a subtype of M.t
That's because A.t' has no relation to M.t, because include does not "preserve" equality, it just literally duplicates the module structure (or signature) and inlines it in place (as fresh types and values). So type M.t doesn't have any relation to A.t and therefore to A.t' (and different record types do not have structural subtyping defined like say objects or modules).
Obvious fix is type t' = private M.t.
UPDATE
It appears the above is not fully correct, because type t' = private M.t in signature and include M type t' = t in implemention do typecheck, so include M preserves the equality (otherwise it couldn't match the signature type t' = private M.t), unlike copypasting the contents of M in the place of include M. But this "obviously" doesn't hold for include module type of which creates fresh types..