初识体验
最开始是通过华为官方发布的cangjie白皮书熟悉了一下cangjie编程语言。通过白皮书指南,第一感受是cangjie几乎涵盖了我开发过程需要的特性。比较亮眼的是强制的Nullable类型处理,和静态编译技术(宏)。于是下意识的认为,动态特性上面也不会让人失望。可实际体验下来发现动态特性非常薄弱,随着开发需求和业务复杂性的提升,对动态特性的需求也变得越来越强烈。对cangjie在动态特性方面的薄弱也感到失望。以及对于aot,宏泛滥如果构建大型项目(有些模块无法编译成lib)在编译速度等方面的担忧。
虽然cangjie几乎涵盖了非常全面的语言特性、框架特性。但没有多少是做的非常突出或者说有亮点的。比如扩展语法、属性语法就非常鸡肋。语言特性方面虽然支持结构体但是并没有看到与之配套的指针特性,或者说安全的指针特性,这就导致结构体的存在变得非常尴尬,让开发者难以取舍。
万事开头难,cangjie拥有非常全面的语言特性,但假以时日完善形成自己特有的特色未来可期。我基于仓颉开源了aspnetcore基础开发实现。有一点可以承认的是,cangjie具有简洁安全快速表达能力,因为使用仓颉来实现aspnetcore代码量并不大:https://gitcode.com/soulsoft/asp。
asp4cj项目介绍
目前支持了aspnetcore中的controller-action。动态终结点,路由,多数据源配置,多日志提供架构,日志过滤器,请求管道-中间件,依赖注入ioc
@Route["/[controller]/[action]"]
public class TestController <: Controller {
@HttpGet
@HttpPut
@HttpPost["create"]
public func hello() {
return json(###"{"msg": "asp"}"###)
}
@HttpGet["/index"]
public func index() {
context.responseBuilder.body("ednpoint:index")
}
}
public class LoggingMiddleware <: Middleware {
let _loggerFactory: LoggerFactory
public init(loggerFactory: LoggerFactory) {
_loggerFactory = loggerFactory
}
public func invoke(context: HttpContext, next: () -> Unit) {
let logger = _loggerFactory.createLogger("asp.1test1.LoggingMiddleware")
logger.info(context.request.url.toString())
logger.warn(context.request.url.toString())
next()
}
}
main(args: Array<String>): Int64 {
var builder = WebHost.createBuilder(args)
builder.logging.addConsole()//添加控制台日志源
let app = builder.build()//构建host
app.useDefaultFiles()//使用默认文件处理
app.useStaticFiles()//使用静态文件处理
//注册EndpointMiddleware负责执行终结点,并同时注册终结点
app.mapEndpoints { endpoints =>
endpoints.mapGet("/asp") {
context => context.responseBuilder.body("hello")
}
//把controller-action处理成endpoint
endpoints.mapController<TestController>()
}
app.run()
return 0
}
个人建议
下面就基于实现asp4cj这个项目来谈一下我对仓颉语言的实际感受和建议。cangjie存在很多迷惑性的语法陷阱,比如可变长参数我就不提出来了,迷惑性非常强很难排查。
关于模式匹配
public interface SomeService {
prop name: ?String
}
public class SomeServiceImpl <: SomeService {
private let _name: ?String
init(name: ?String) {
this._name = name
}
public prop name: ?String {
get() {
_name
}
}
}
main(): Int64 {
let instance: ?SomeServiceImpl = SomeServiceImpl("cangjie")
let name = instance?.name
if (name == None) {
println("name not null")
}else {
println("name is null")
}
return 0
}
这个demo让我感觉十分无语和不可理解。既然支持了?运算符,instance?.name推断出来的类型居然是Option<Option
这就给下面的模式解构带来了负担,下面的两种解构都存在嵌套,让人很难受。
main(): Int64 {
let instance: ?SomeServiceImpl = SomeServiceImpl("cangjie")
if (let Some(Some(name)) <- instance?.name) {
println(name)
}
if (let Some(obj) <- instance) {
if (let Some(name) <- obj.name) {
println(name)
}
}
return 0
}
类型转换
main(): Int64 {
let any:Any = Object()
if (let Some(obj) <- (any as Object)) {
println("success")
}
//简写
if (let obj:Object <- any) {
println("success")
}
return 0
}
通过这个案例,可以给人一种语法迷惑,就是if let语法可以实现两个类型之间的转换,于是会有人写出这样的代码。
//错误的认为if-let可以,完成Object <- ?Object之间的类型转换,但实际不会成功,需要我们区分你是在进行类型转换还是模式解构。即cangjie的if-let只能做模式解构,它的简化形式,会让人误导它能够进行类型转换
main(): Int64 {
let any:?Object = Object()
if (let obj:Object <- any) {
println("success")
}
return 0
}
个人建议应该对if-let进行增强使得它可以进行Object <- ?Object之间的转换。减少迷惑性代码的发生。而且同时简化了类型转换。if本来就应该能支持判定是否能转换到目标类型,在可以转换的情况下,转换到目标类型,减少解构路径。个人建议要么取消if-let的隐式问题,要么对其进行增强。
//对于这个场景路径就会变得很长,嵌套也会变得很多
main(): Int64 {
let anyNullable:?Any = Object()
if (let Some(any) <- anyNullable) {
//注意if-let不支持类型转换,类型转换只有一种就是as,但是这里涉及到了一个隐式语法
if (let obj:Object <- any) {
println("success")
}
}
return 0
}
关于扩展语法
目前扩展语法个人感觉非常鸡肋,有点画大饼的感觉,还不如Utilities来的方便,比如要给HttpContext扩展函数,使用起来和声明起来都很繁琐(需要定义一个接口+实现,导出的时候也需要导出接口和被扩展类型)。而且目前不能对接口直接扩展。比如我们希望给所有的实现了Iterable接口的派生类扩展一个打印函数,目前无法实现。只能通过扩展Iterator来实现,导致调用不够简洁,为此cangjie还吧Iterator变成了abstract class实现Iterable,这让人感觉非常混乱。实际情况是很多第三方框架暴露出来的都是一些接口。这使得我们无法面向接口设计api。std库的Iterator尚且如此。
关于属性
从上面的SomeService接口来看,你无法在接口上声明name是一个只读的属性。而且属性的语法过于啰嗦,必须要使用完整写法,即需要同时定义一个对应字段_name。
关于函数隐式返回和返回类型推断
func name() {
0
}
这种写法如果不加以控制,会造成隐患,可能你一个不注意的改动,就会导致难以排查的风险,多个if-else的函数体你找不到退出函数的分支。其实声明类型和一个return,并不会让人觉得繁琐。
从这里可以看出设计师的初衷是好的简化书写,但是这一点和扩展语法设计初衷违背。这么繁琐的扩展语法为了安全,到了函数隐式类型推断和返回安全性就被忽略了。看起来这两个点不像是一个人设计的。
关于动态技术
由于cangjie的采用的aot首发,导致动态特性非常薄弱,而cangjie语言的定位又是应用开发。社区妄图通过静态技术来进行弥补,导致连std库连个json序列化,反序列化器都没能提供。而且将来的orm也难以做到像C#那样遍地开花。个人感觉像是定位不清晰,花那么大精力对静态编译技术投入,可是产出却不太客观。赋能也很有限,因为编写心智负担极大。反过来如果在动态技术上进行投入,那么产出肯定相对比较大,比如实现运行阶段的cangjie表达式解析等技术,为后来的orm以及其它的DSL场景提供了可能性。
总结
cangjie语言特性丰富,有很多的可能性,以及安全特性,但存在诸多不足之处,希望日后日益完善。建议应该往动态特性上面加大投入,以及引入安全的指针操作,比如C#的ref等等。静态编译技术不能作为语法不足的找补,只是一种工具和手段,实际赋能有限。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性