Why isn't Python NewType compatible with isinstance and type? - python-typing

This doesn't seem to work:
from typing import NewType
MyStr = NewType("MyStr", str)
x = MyStr("Hello World")
isinstance(x, MyStr)
I don't even get False, but TypeError: isinstance() arg 2 must be a type or tuple of types because MyStr is a function and isinstance wants one or more type.
Even assert type(x) == MyStr or is MyStr fails.
What am I doing wrong?

Cross-reference: inheritance from str or int
Even more detailed in the same question: https://stackoverflow.com/a/2673802/1091677
If you would like to subclass Python's str, you would need to do the following way:
class MyStr(str):
# Class instances construction in Python follows this two-step call:
# First __new__, which allocates the immutable structure,
# Then __init__, to set up the mutable part.
# Since str in python is immutable, it has no __init__ method.
# All data for str must be set at __new__ execution, so instead
# of overriding __init__, we override __new__:
def __new__(cls, *args, **kwargs):
return str.__new__(cls, *args, **kwargs)
Then:
x = MyStr("Hello World")
isinstance(x, MyStr)
returns True as expected

As at python 3.10...
I'd speculate that the answer to
"Why isn't Python NewType compatible with isinstance and type?"
... is "It is a limitation of NewType".
I'd speculate that the answer to
"What am I doing wrong?"
... is "nothing". You are assuming NewType makes a new runtime type, it appears that it doesn't.
And for what it's worth, I wish it did work.

maybe you want a type that is methods like str does but is not a str?
A simple way to get that effect is just:
class MyStr:
value:str
def __init__(self,value:str):
self.value=value
pass
... but that means using all the string methods is "manual", e.g.
x=MyStr('fred')
x.value.startswith('fr')
... you could use #dataclass to add compare etc.
This is not a one-size-fits-all answer, but it might suit your application.

then make that simple
class MyStr:
value:str
def __init__(self,value:str):
self.value=value
...generic like Str in (incomplete) https://github.com/urnest/urnest/blob/master/xju/newtype.py
... and you can write:
class MyStrType:pass; class MyOtherStrType:pass
class MyStr(Str[MyStrType]):
pass
class MyOtherStr(Str[MyOtherStrType]):
pass
x=MyStr('fred')
y=MyOtherStr('fred')
x < y # ! mypy error distinct types MyStr and MyOtherStr
That's what I was after, which might be what you were after? I have to provide Int,Str separately but in my experience distinct int, str, float, bool, bytes types give a lot of readability and error-rejection leverage. I will add Float, Bool, Bytes to xju.newtype soon and give them all the full set of methods.

looks might have been "fixed" in python 3.10:
https://docs.python.org/3/library/typing.html?highlight=typing#newtype
says:
Changed in version 3.10: NewType is now a class rather than a function. There is some additional runtime cost when calling NewType over a regular function. However, this cost will be reduced in 3.11.0.
I don't have 3.10 handy as I write this to try your example.

Related

How to reveal generic type

I have the following generic type definition:
# typed: true
class A
extend T::Sig
extend T::Generic
Value = type_member
sig { params(value: Value).void }
def initialize(value)
#value = value
end
sig { returns(Value) }
def value
#value
end
end
When I reveal the type of #value, I'm expecting it is Integer, but it's T.untyped:
v = A.new(42)
T.reveal_type(v.value) #=> Revealed type: T.untyped https://srb.help/7014
I understand it's possible to specify the type of the argument explicitly A[Integer].new(42), but that way I can't put keep type information separately in .rbi file.
What is the right way annotating generic types?
A[Integer].new(42) is the right way to specify generic type.
You probably expect sorbet to infer type Integer from the input (42). However, sorbet doesn’t work that way. You have to specify the generic type as A[Integer] and sorbet will use that to validate the type of the input (42) and output (#value) at run time.
I understand it's possible to specify the type of the argument explicitly A[Integer].new(42), but that way I can't put keep type information separately in .rbi file.
It is probably impossible to separate type construct (or sorbet construct) and your code cleanly like that for generic type. As you’d see even for T::Array or T::Hash, you also have to specify the type the same way. And for constant, you’d frequently need to use T.let.

Ruby ! Methods or Procs

I'm wanting to modify a variable in place, replicating the methodname! syntax as opposed to reassigning a new modified value to the same var. Can I do this with a proc? I'm still learning procs and see them as quite useful is used properly.
a = "Santol bag 85.88 www.example.com/products/16785
Shaddock kg 2.94 www.example.com/products/4109
Palm Fig 5kg 94.34 www.example.com/products/23072
Litchee lb 95.85 www.example.com/products/2557"
a = a.split("\n")
linebreak = Proc.new { |text| text.split("\n") }
linebreak![a]
that first reassignment seems cumbersome. The proc version I would like to see if I can perform it inline. Is this possible?
This is surely possible, you just need to modify the string inplace
linebreak = ->(text) { text.replace text.split("\n").join(",") }
a = "foo\nbar"
linebreak[a]
#⇒ "foo,bar"
a
#⇒ "foo,bar"
What is not possible, is to change the class in place, that’s why split won’t work (called on a string, it returns an array.)
methodname! is just a convention - usually there are two flavours of the same method - one without bang and one with bang. If you want to have a proc that mutates its params, you need to implement it using mutating methods.
And in this case it's not possible, because you're trying to transform a string into an array. You have to reassign the variable:
linebreak = Proc.new { |text| text.split("\n") }
a = linebreak.call(a)

Scala -> Ruby conversion: Confusing output

Ruby Code:
def fnAdd(x)
return ->(y) { x + y }
end
add1 = 1.method(:fnAdd)
puts add1.call(10)
Output: Proc:0x007f52658d3330#main.rb:2 (lambda)
I am having issues getting the desired output in the above code.
I'm basically trying to write the following Scala code (which calls a function that returns another function) in Ruby.
Scala Code:
def fnAdd (x:Int) = {
(y:Int) => x + y
}
var add1 = fnAdd (1)
var add2 = fnAdd (2)
println(add1(10))
println(add2(3))
Output: 11 5
I've made an attempt at converting the code to Ruby but I'm not sure if it is correct. I don't understand the output, which appears to be some kind of proc object.
Could someone please explain what I need to change to get the desired output?
I'm not sure how your first example is even running, as it produces a NameError on my machine. Regardless, #method is intended for accessing methods on specific objects. You've defined a standalone method which is already curried, not one inside of the Fixnum class. So you simply need to call it as a method.
add1 = fnAdd(1)
Also, Ruby has the same behavior as Scala with regard to returning the last expression in a method, so you don't need to use return in this case.
Edit:
Thanks to #JörgWMittag for pointing out a few flaws here. Defining #fnAdd at the top-level makes it a private instance method on Object. Since everything in Ruby is an object, Fixnum inherits from the Object class. Thus, 1.method(:fnAdd) is simply giving you the fnAdd method without actually passing it any arguments. Thus, it still expects to be called twice.
fnAddMethod = 1.method(:fnAdd)
add1 = fnAddMethod.call(1)
puts add1.call(10)
However, this would be extremely unidiomatic, so it's best to stick with the simpler solution.

Liftable for function literal

Is there a way to make a Liftable for a functional literal (with 2.11)? If I have
case class Validator[T](predicate: T => Boolean)
val predicate = (s: String) => s.startsWith("Hi")
then I want to be able to quasiquote predicate too:
q"new Validator($predicate)"
I hoped to magically create a Liftable with an underscore. But that was a little too optimistic:
implicit def liftPredicate[T: Liftable](f: T => Boolean) =
Liftable[T => Boolean]{ f => q"$f(_)" }
I couldn't figure out from looking at StandardLiftables how I could solve this one.
Another way of looking at it:
Say I want to create instances from the following class at compile time with a macro:
abstract class ClassWithValidation {
val predicate: String => Boolean
def validate(s: String) = predicate(s)
}
and I retrieve a functional literal from somewhere else as a variable value:
val predicate = (s: String) => s.startsWith("Hi")
Then I want to simply quasiquote that variable into the construction:
q"""new ClassWithValidation {
val predicate = $predicate
// other stuff...
}"""
But it gives me this error:
Error:(46, 28) Can't unquote String => Boolean, consider providing an
implicit instance of Liftable[String => Boolean]
Normally I can just make such implicit Liftable for a custom type. But I haven't found a way doing the same for a functional literal. Is there a way to do this or do I need to look at it another way?
From what I understand, you're trying to go from a function to an abstract syntax tree that represents its source code (so that it can be spliced into a macro expansion). This is a frequent thing that people request (e.g. it comes up often in DSLs), but there's no straightforward way of achieving that in our current macro system.
What you can do about this at the moment is to save the AST explicitly when declaring a function and then load and use it in your macro. The most convenient way of doing this is via another macro: https://gist.github.com/xeno-by/4542402. One could also imagine writing a macro annotation that would work along the same lines.
In Project Palladium, there is a plan to save typechecked trees for every program being compiled. This means that there will most likely be a straightforward API, e.g. treeOf(predicate) that would automatically return abstract syntax tree comprising the source of the predicate. But that's definitely not something set in stone - we'll see how it goes, and I'll report back on the progress during this year's ScalaDays.

Overriding default range output

Right now the code below produces the output below it, but how would I override the default output to a more logical one for my given situation. I understand that I could just append the string "Hz" after the range but I want to incorporate this into a module which can be included to the Range class when needed or for use with refinements.
Code:
("20Hz"..."40Hz").each { |hz| p hz }
Output:
"20Hz"
"20Ia"
"20Ib"
...etc
Wanted output:
"20Hz"
"21Hz"
"22Hz"
...etc
This is absolutely a bad idea, but just for the sake of experimenting:
class String
alias_method :succ_orig, :succ
def succ
self.gsub(/\d+/, &:succ_orig)
end
end
p ("20Hz".."40Hz").to_a
#=> ["20Hz", "21Hz", "22Hz", "23Hz", "24Hz", "25Hz", "26Hz", "27Hz", "28Hz", "29Hz", "30Hz", "31Hz", "32Hz", "33Hz", "34Hz", "35Hz", "36Hz", "37Hz", "38Hz", "39Hz", "40Hz"]
As you can see, it is not the Range class that should be altered, but String#succ method.
But in real project, you better create a class for your Hertz-strings and define its succ method appropriately.
I think its quite simple.
("20"..."40").each { |hz| p hz + 'Hz'}
I would recommend creating your own function or class for this rather that changing the way in which Ruby ranges behave. There is probably a lot of other code that depends on ranges working in a specific way, and changing the range definition would result in that code breaking. You might want to aim for something like this:
HzRange.new("20Hz", "40Hz").each{ |hz| p hz }
The creation of the HzRange class is up to you, but you should probably delegate to the Array or Range object so that you can inherit some default behavior like Enumerable.

Resources