标签: static-typing

厌倦了非语义测试以弥补动态类型 - 建议?

在我开始学习计算机工程之前,我曾经在Rails(之前的PHP)中做了很多Web编程.

从那时起,我在C中完成了大量的学习工作,并在Objective-C(Mac的东西)中完成了一些个人工作.我学会了喜欢静态打字.

但是现在我不得不做一些专业的网页开发(自由职业),并再次获得了Rails.我发现编写非语义类型检查测试真的很烦人.我从C和Objective-C编译器中免费获得这些.我喜欢点击Build并让系统检查我的所有代码,看A可以调用B,B可以调用一些模糊的库C等.我所要做的就是测试语义.但是对于Rails,我是编译器.:(

有人走过同样的道路吗?我是使用C#和Java + x框架进行Web开发ASP.NET MVC的唯一选择吗?寻找一些建议,甚至是一些同情......:P

顺便说一句,我特别提到了Rails而不是Ruby,因为我不介意Ruby的动态特性,比如脚本或其他简单的东西.但由于Rails依赖于如此多的宝石,并且由于通常会添加许多其他宝石,因此动态类型成为一个问题.

谢谢!

编辑:

我跟进了pst的建议并调查了Scala.在阅读由Scala创作者Martin Odersky撰写的"Scala编程"一书时,我发现了这一段文本,它在很多方面表达了我的关注和更多.非常有趣的阅读.

摘自Martin Odersky的Scala编程第52页:

Scala是静态类型的

静态类型系统根据它们保存和计算的值的类型对变量和表达式进行分类.Scala作为一种具有非常先进的静态类型系统的语言脱颖而出.从嵌套类类型很像Java的制度开始,它可以让你参数化类型的泛型,使用交叉类型组合,并隐藏的使用抽象类类型的详细信息.这些为构建和组合您自己的类型奠定了坚实的基础,因此您可以设计同时安全且灵活使用的界面.

如果你喜欢动态语言如Perl,Python和Ruby,或Groovy,你可能会发现它有点怪Scala的静态类型系统列为其强项之一.毕竟,有些人认为缺少静态类型系统是动态语言的主要优势.针对静态类型最常见的论据是,他们做节目太冗长,阻止程序员表达出他们希望,使软件系统动态修改不可能某些模式.

但是,往往这些参数不违背静态类型一般的想法,而是针对特定类型的系统,该系统被认为过于冗长或太不灵活.例如,艾伦·凯,Smalltalk语言的发明者,曾经说过:"我不反对的类型,但我不知道任何类型的systemsthat的是不是一个完整的痛苦,所以我还是喜欢动态类型."

我们希望能说服你在这本书Scala的类型系统是远不是一个"完整的痛"事实上,它解决了很好的两个关于静态类型的普遍关注:通过类型推断避免冗长和灵活性,通过模式匹配获得以及编写和撰写类型的几种新方法.有了这些障碍,可以更好地理解静态类型系统的经典优势.其中最重要的好处是程序抽象,安全重构和更好的文档的可验证属性.

可验证的属性

静态类型系统可以证明不存在某些运行时错误.例如,他们可以证明属性,如:布尔从来没有加入到整数; 私有变量不能从他们的课外访问; 函数应用于正确数量的参数; 只有字符串被添加到一组字符串中.

今天的静态类型系统没有检测到其他类型的错误.例如,它们通常不会检测非终止函数,数组边界违规或除零.他们也不会检测到你的程序不符合其规范(假设有一个规范,那就是!).因此,一些人认为静态类型系统不是很有用.该论点认为,由于此类型系统只能检测简单错误,而单元测试提供更广泛的覆盖范围,为什么还要使用静态类型呢?

我们认为这些论点忽视了这一点.虽然静态类型系统当然不能取代单元测试,但它可以通过处理一些本来需要测试的属性来减少所需的单元测试数量.同样,单元测试不能取代静态类型.毕竟,正如Edsger Dijkstra所说,测试只能证明存在错误,而不是缺席.因此,静态类型提供的保证可能很简单,但它们是形式的真正保证,没有多少测试可以提供.

安全的重构

静态类型系统提供了一个安全网,使您可以高度自信地对代码库进行更改.例如,考虑一个为方法添加附加参数的重构.在静态类型语言中,您可以进行更改,重新编译系统并简单地修复导致类型错误的所有行.完成此操作后,您确定已找到所有需要更改的位置.对于许多其他简单的重构,例如更改方法名称或将方法从一个类移动到另一个类,这同样适用.在所有情况下,静态类型检查将提供足够的保证,使新系统像旧系统一样工作.

文档

静态类型是程序文档,编译器会检查它是否正确.与普通注释不同,类型注释永远不会过时(至少在包含它的源文件最近通过编译器时不会).此外,编译器和集成开发环境可以使用类型注释来提供更好的上下文帮助.例如,集成开发环境可以通过确定进行选择的表达式的静态类型并查找该类型的所有成员来显示可供选择的所有成员.

c# java scala static-typing ruby-on-rails

9
推荐指数
1
解决办法
421
查看次数

Java泛型,紧密有界参数类型

我希望有一种方法具有类似于method(T1 t1, T2 t2) T2是-T1和/或T1是-T2 的签名 .我不希望T1和T2都是T的情况,但两者都不是 - 另一个.我希望最允许的类型在继承树中以T1或T2的最高值为界.我使用的是Java 6.

下面是尝试显示一些所需的用例

class Experiment
{
  static List genericList = new ArrayList();
  static ArrayList arrayList = new ArrayList();

  static class Test1 { }
  static class Test2 extends Test1 { }
  static class Test3 extends Test1 { }

  static <T> T experiment0(T expected, T actual)
  {
    return actual;
  }

  /** Almost works, but want arbitrary ordering. Cannot overload due to erasure. */
  static <T, S extends T> S experiment1(T expected, S actual) …
Run Code Online (Sandbox Code Playgroud)

java generics static-typing templating

9
推荐指数
1
解决办法
208
查看次数

Python:如何按值为 bool 参数编写typing.overload 装饰器

我要问的示例代码如下。
互联网上没有一个例子试图重载参数值。
其中一个参数是 bool 值,我想基于 bool 值而不是通常的参数类型重载一个方法。

from typing import overload, Union

@overload
def myfunc(arg:bool=True)-> str: ...

@overload
def myfunc(arg:bool=False)-> int: ...

def myfunc(arg:bool)->Union[int, str]:
    if arg: return "something"
    else: return 0
Run Code Online (Sandbox Code Playgroud)

上面示例代码中的重载代码是否正确?
你能给出一个提到这种重载的例子/博客/来源吗,因为我在 Python 文档和pep-484 中找不到任何东西

我发现一种可能的方法是typing.Literal使用最新的 python 文档(自 python v3.8 起)

from typing import overload, Union, Literal

@overload
def myfunc(arg:Literal[True])-> str: ...

@overload
def myfunc(arg:Literal[False])-> int: ...

def myfunc(arg:bool)->Union[int, str]:
    if arg: return "something"
    else: return 0

Run Code Online (Sandbox Code Playgroud)

但是我还不能转移到 python 3.8,因为我正在处理仍然在 python 3.6 上的生产代码,很快就会升级到 …

python overloading static-typing mypy pyright

9
推荐指数
2
解决办法
4617
查看次数

如何在运行时检查 TypeVar 的类型

我有一个通用类Graph[Generic[T], object]
我的问题是,是否有任何函数返回作为泛型传递给类的类型Graph

>>> g = Graph[int]()
>>> magic_func(g)
<class 'int'>
Run Code Online (Sandbox Code Playgroud)

python generics static-typing python-typing

9
推荐指数
1
解决办法
5521
查看次数

可选参数之后的重载

我有一个类Animal,该类的方法foo根据inplace可选参数后面的布尔参数具有不同的返回类型bar。我想重载该函数,以便在已知inplace的值的情况下知道返回类型

这是我的代码:

# main.py

from __future__ import annotations

from typing import Optional, overload, Literal 


class Animal:
    @overload
    def foo(self, bar=..., inplace: Literal[False]=...) -> Animal:
        ...

    @overload
    def foo(self, bar=..., inplace: Literal[True]=...) -> None:
        ...

    def foo(
        self, bar=None, inplace: bool = False
    ) -> Optional[Animal]:
        ...


reveal_type(Animal().foo(bar='a'))
reveal_type(Animal().foo(inplace=True))
reveal_type(Animal().foo(inplace=False))
Run Code Online (Sandbox Code Playgroud)
$ mypy main.py
main.py:8: error: Overloaded function signatures 1 and 2 overlap with incompatible return types
main.py:21: note: Revealed type is …
Run Code Online (Sandbox Code Playgroud)

python static-typing mypy

9
推荐指数
1
解决办法
2392
查看次数

用鸭子型语言模拟静态类型的各个方面

在我目前的工作中,我正在构建一套严重依赖于对象的Perl脚本.(bless()在Hash上使用Perl 来尽可能接近OO)

现在,由于缺乏更好的方法,我公司的大多数程序员都不是很聪明.更糟糕的是,他们不喜欢阅读文档,并且似乎在理解其他人的代码时遇到了问题.牛仔编码就是这里的游戏.每当他们遇到问题并尝试修复它时,他们就会想出一个可怕的解决方案,实际上什么也解决了,通常会让事情变得更糟.

坦率地说,这导致我不相信用鸭子类型语言编写的代码.作为一个例子,我看到太多问题,他们没有得到滥用对象的明确错误.例如,如果type A有成员foo,并且他们执行类似的操作instance->goo,则他们不会立即看到问题.它将返回一个null/undefined值,它们可能会浪费一个小时来查找原因.然后最终改变别的东西,因为他们没有正确识别原始问题.

因此,我正在集思广益,以保持我的脚本语言(快速开发是一个优势),但在对象未正确使用时给出明确的错误消息.我意识到,由于没有编译阶段或静态类型,错误必须在运行时.我很好,只要用户得到一个非常明确的通知说"这个对象没有X"

作为我的解决方案的一部分,我不希望在尝试使用它之前检查方法/变量是否存在.

尽管我的工作是在Perl中,但我认为这可能与语言无关.

language-agnostic perl duck-typing static-typing

8
推荐指数
2
解决办法
651
查看次数

为什么接口必须用Java声明?

有时我们有几个类具有相同签名的某些方法,但是它们与声明的Java接口不对应.例如,两者JTextFieldJButton(在其他几个中 javax.swing.*)都有一种方法

public void addActionListener(ActionListener l)
Run Code Online (Sandbox Code Playgroud)

现在,假设我希望对具有该方法的对象执行某些操作; 那么,我想要一个界面(或者自己定义它),例如

  public interface CanAddActionListener {
      public void addActionListener(ActionListener l);
  }
Run Code Online (Sandbox Code Playgroud)

所以我可以写:

  public void myMethod(CanAddActionListener aaa, ActionListener li) {
         aaa.addActionListener(li);
         ....
Run Code Online (Sandbox Code Playgroud)

但是,遗憾的是,我不能:

     JButton button;
     ActionListener li;
     ...
     this.myMethod((CanAddActionListener)button,li);
Run Code Online (Sandbox Code Playgroud)

这个演员是非法的.编译器知道JButton 不是 a CanAddActionListener,因为类没有声明实现该接口...... 但是它"实际"实现了它.

这有时会带来不便 - 而Java本身已经修改了几个核心类来实现由旧方法构成的新接口(String implements CharSequence例如).

我的问题是:为什么会这样?我理解声明一个类实现接口的实用程序.但无论如何,看看我的例子,为什么编译器不能推断出类JButton"满足"接口声明(查看它内部)并接受转换?这是编译器效率的问题还是存在更多基本问题?

我对答案的总结:这是一个Java可以允许一些"结构类型"(一种鸭子打字 - 但在编译时检查)的情况.它没有.除了一些(对我来说不清楚)性能和实现困难之外,还有一个更为基本的概念:在Java中,接口的声明(以及一般来说,所有内容)并不仅仅是结构性的(具有方法)这些签名)但语义:这些方法应该实现一些特定的行为/意图.因此,一个在结构上满足某些接口的类(即,它具有所需签名的方法)并不一定在语义上满足它(一个极端的例子:回想一下"标记接口",它甚至没有方法!).因此,Java可以断言一个类实现了一个接口,因为(并且只是因为)已经显式声明了它.其他语言(Go,Scala)有其他语言.

java duck-typing static-typing interface structural-typing

8
推荐指数
2
解决办法
748
查看次数

编译器丢弃我的类型转换?

我为使这段代码工作而必须做的事感到困惑.似乎编译器优化了我需要的类型转换,或者在这里我还有其他一些我不理解的东西.

我有各种对象存储在实现接口的数据库中Foo.我有一个对象,bar它保存我用来检索我的Foo对象的数据.bar有这些方法:

Class getFooClass()

Long getFooId()
Run Code Online (Sandbox Code Playgroud)

我将类和ID传递给具有此签名的方法,该方法委托给hibernate,后者根据其类和ID检索主题:

public <T> T get(Class<T> clazz, Serializable id);
Run Code Online (Sandbox Code Playgroud)

有不同的实现者Foo,其中一些hibernate对象有Longid,而其他有Integerid.虽然这种方法接受了,但更远的地方最好接受正确的方法.因此,当我尝试调用get()带有Integerid 的对象时,如下所示,我可以理解地抱怨我提供了Long一个Integer需要的位置:

    get(bar.getFooClass(), bar.getFooId());
Run Code Online (Sandbox Code Playgroud)

这里有没有休眠的问题,我只需要提供一个Integer地方的Integer需要ID和Long其中一个Long是必需的ID.所以我添加了一个方法bar,hasLongId()和试过这:(在这一点上,你可能会想,这不是一个很好的设计,但现在这不是我的问题)

get(bar.getFooClass(),
    bar.hasLongId() ? bar.getFooId() : bar.getFooId().intValue());
Run Code Online (Sandbox Code Playgroud)

它仍然抱怨我提供了一个Long.这看起来很奇怪.然后我尝试了这个:

get(bar.getFooClass(),
    bar.hasLongId() ? bar.getFooId() 
                    : new Integer(bar.getFooId().intValue()));
Run Code Online (Sandbox Code Playgroud)

同样的错误!怎么会这样?所以我介入调试器,是的,它逐步完成intValue()并通过Integer构造函数,但是在get方法中,传递的参数实际上是一个Long- Long从返回的同一个对象getFooId(). …

java static-typing

8
推荐指数
1
解决办法
79
查看次数

从带mypy注释的python函数返回None,多种返回类型

我来自打字稿背景。我正在将静态类型检查引入到我正在研究的python项目中(使用mypy)。

在Typescript中,从带有注释以返回其他内容(例如字符串)的函数中返回null是有效的:

function test(flag: boolean): string {
    if(flag) {
        return 'success';
    } else {
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

对函数进行注释以使其具有多种潜在的返回类型(例如字符串或布尔值)也是有效的:

function test(flag: boolean): string | boolean {
    if(flag) {
        return 'success';
    } else {
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,在使用mypy的python中,不允许从注释为return的函数返回None str

def test(flag: bool) -> str:
    if flag:
        return 'success'
    else:
        return None
        # [mypy] error:Incompatible return value type (got "None", expected "str")
Run Code Online (Sandbox Code Playgroud)

此外,我看不到注释多种返回类型的方法,即str | None

我应该如何使用mypy处理类似的事情?从错误状态返回None的函数遍布我的代码库。

python static-typing function return-type mypy

8
推荐指数
1
解决办法
1476
查看次数

Python 如何使用 __wrapped__ 键入提示 Callable

当传递函数时,我通常用 来提示它们typing.Callable

的文档声明collections.abc.Callable它有四种 dunder 方法:

类 collections.abc.Callable

分别提供方法__contains__()__hash__()__len__()和的类的 ABC __call__()

有一次,我想检查__wrapped__函数上是否有属性。通过检查,这在运行时工作得很好hasattr(func, "__wrapped__")

当使用 进行静态类型检查时mypy,它会报告:error: "Callable[..., Any]" has no attribute "__wrapped__" [attr-defined]。这对我来说很有意义,因为Callable不应该有__wrapped__属性。

如何正确输入Callable带有__wrapped__属性的提示 a?我可以做一些其他类型的提示或解决方法吗?


代码示例

我正在使用mypy==0.782Python==3.8.2

from functools import wraps
from typing import Callable


def print_int_arg(arg: int) -> None:
    """Print the integer argument."""
    print(arg)


@wraps(print_int_arg)
def wrap_print_int_arg(arg: int) -> None: …
Run Code Online (Sandbox Code Playgroud)

python static-typing type-hinting callable-object mypy

8
推荐指数
1
解决办法
2794
查看次数